首页 > 代码库 > HDU 4539 郑厂长系列故事――排兵布阵(状态压缩)

HDU 4539 郑厂长系列故事――排兵布阵(状态压缩)

郑厂长系列故事——排兵布阵

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 1954    Accepted Submission(s): 701


Problem Description
  郑厂长不是正厂长
  也不是副厂长
  他根本就不是厂长
  事实上
  他是带兵打仗的团长

  一天,郑厂长带着他的军队来到了一个n*m的平原准备布阵。
  根据以往的战斗经验,每个士兵可以攻击到并且只能攻击到与之曼哈顿距离为2的位置以及士兵本身所在的位置。当然,一个士兵不能站在另外一个士兵所能攻击到的位置,同时因为地形的原因平原上也不是每一个位置都可以安排士兵。
  现在,已知n,m 以及平原阵地的具体地形,请你帮助郑厂长计算该阵地,最多能安排多少个士兵。
 

Input
输入包含多组测试数据;
每组数据的第一行包含2个整数n和m (n <= 100, m <= 10 ),之间用空格隔开;
接下来的n行,每行m个数,表示n*m的矩形阵地,其中1表示该位置可以安排士兵,0表示该地形不允许安排士兵。
 

Output
请为每组数据计算并输出最多能安排的士兵数量,每组数据输出一行。
 

Sample Input
6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 

Sample Output
2
 

题解:
1.曼哈顿距离:两点(x1,y1)(x2,y2)的曼哈顿距离为|x1-x2|+|y1-y2|;
2.假如一个点要安排士兵,我们要考虑的是曼哈顿距离为二的几个点是否会造成冲突,即若(x,y)安排人,则(x-2,y)(x-1,y+1),(x,y+2),(x+1,y+1),(x+2,y),(x+1,y-1),(x,y-2),(x-1,y-1)不能有人。然后就是状态方程了:dp[i][j][k] = Max(d[i][j][k],dp[i - 1][k][l] + sum[j]);

My Code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<map>

#define N 100010
#define Mod 10000007
#define lson l,mid,idx<<1
#define rson mid+1,r,idx<<1|1
#define lc idx<<1
#define rc idx<<1|1
const double EPS = 1e-11;
const double PI = acos ( -1.0 );
const double E = 2.718281828;
typedef long long ll;

const int INF = 1000010;

using namespace std;

int cnt[250], sum[250];///保存在一行中所有符合情况的值
int mp[110];//用来表示每行的情况,1代表不能安排士兵,0可以
int len, n, m;
int dp[110][250][250];

bool ok ( int x )
{
    if ( x & ( x << 2 ) ) return false;///同一行距离为2的有没有冲突
    return true;
}

int getsum ( int x )///没有冲突的时候计算1的个数
{
    int ans = 0;
    while ( x > 0 )
    {
        if ( x & 1 )//最后一位是1
            ans++;
        x >>= 1;///右移一位
    }
    return ans;
}

void finds()///算出所有符合情况的值
{
    len = 0;
    memset ( cnt, 0, sizeof cnt );
    for ( int i = 0; i < ( 1 << m ); i++ )
    {
        if ( ok ( i ) )
        {
            cnt[len] = i;
            sum[len++] = getsum ( i );
        }
    }
    //cout << len << endl;
}

int main()
{
    //freopen("in.txt","r",stdin);
    while ( cin >> n >> m )
    {
        //m = 10;
        finds();
        int c;
        memset ( mp, 0, sizeof ( mp ) );
        for ( int i = 0; i < n; i++ )
        {
            for ( int j = 0; j < m; j++ )
            {
                scanf ( "%d", &c );///为了方便计算,吧、把1存为0,把0存为1
                if ( !c )
                    mp[i] |= ( 1 << j );
            }
        }
        memset ( dp, -1, sizeof ( dp ) );
        for ( int i = 0; i < len; i++ )///先处理第一行
        {
            if ( ! ( cnt[i]&mp[0] ) )
                dp[0][i][0] = sum[i];
        }
        for ( int i = 1; i < n; i++ )
        {
            for ( int j = 0; j < len; j++ )
            {
                if ( cnt[j]&mp[i] )///与mp[i]有冲突
                    continue;
                for ( int k = 0; k < len; k++ )///i-1行的情况
                {
                    if ( ( ( cnt[k] << 1 ) &cnt[j] ) || ( ( cnt[k] >> 1 ) &cnt[j] ) ) continue;///左右一个单位的对角上有人的情况
                    for ( int l = 0; l < len; l++ )///i-2行
                    {
                        if ( cnt[j]&cnt[l] )///第i行与第2行有冲突
                            continue;
                        if ( ( ( cnt[l] << 1 ) &cnt[k] ) || ( ( cnt[l] >> 1 ) &cnt[k] ) ) continue;///i-1行与i-2行
                        if ( dp[i - 1][k][l] == -1 )
                            continue;
                        if ( dp[i - 1][k][l] + sum[j] > dp[i][j][k] )
                            dp[i][j][k] = dp[i - 1][k][l] + sum[j];
                    }
                }
            }
        }
        int ans = -1;
        for ( int i = 0; i < len; i++ )
            for ( int j = 0; j < len; j++ )
                if ( ans < dp[n - 1][i][j] )
                    ans = dp[n - 1][i][j];
        cout << ans << endl;
    }
    return 0;
}


HDU 4539 郑厂长系列故事――排兵布阵(状态压缩)