HDU 4539 郑厂长系列故事——排兵布阵(状压dp)

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

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


 

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

题意:中文题略。

思路:

先预处理出同行的合法状态,然后状态压缩的过程中对不合法状态进行剪枝,这样枚举这一行、上一行、上上行的状态进行相应的状态转移,实际复杂度非常低。

代码:

#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<string>
#include<queue>
#include<vector>
#include<map>
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(register int i=(a);i<=(b);++i)
using namespace std;
const int maxn=12;
const ll mo=1e8;
int n,m,k,mm;
int a[110][maxn];
int tmp[110];
int zt[110][1026],cnt;
int dp[2][1026][1026];
int c[1026];
int cal(int x)
{
    int sum=0;
    while(x)
    {
        x&=(x-1);
        sum++;
    }
    return sum;
}
void init()
{
    rep(i,1,n)
    {
        zt[i][0]=0;
        rep(j,0,mm)
        if((j|tmp[i])==j){
            int x=j^tmp[i];
            if(x&(x>>2)) continue;
            if(x&(x<<2)) continue;
            zt[i][++zt[i][0]]=j;
            if(i==1) dp[i][0][j]=c[x];
            //if(i==2)cout<<j<<" "<<tmp[i]<<" "<<x<<endl;
        }
    }
}
int main()
{
    rep(i,0,1024) c[i]=cal(i);
    int T,cas=1;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        mm=(1<<m)-1;
        rep(i,1,n)
        {
            tmp[i]=0;
            rep(j,0,m-1)
            {
                int x;
                scanf("%d",&x);
                if(!x) tmp[i]|=(1<<j);
            }
        }

        memset(dp,-1,sizeof(dp));

        init();
        zt[0][0]=1;
        zt[0][1]=0;
        rep(i,2,n)
        {
            int od=((i-1)&1),nw=od^1;
            memset(dp[nw],-1,sizeof(dp[nw]));
            rep(jj,1,zt[i-2][0])
            {
                int j=zt[i-2][jj]^tmp[i-2];
                rep(kk,1,zt[i-1][0])
                {
                    int k=zt[i-1][kk]^tmp[i-1];
                    if(dp[od][j|tmp[i-2]][k|tmp[i-1]]==-1) continue;
                    if(j&(k>>1)) continue;
                    if(j&(k<<1)) continue;
                    rep(tt,1,zt[i][0])
                    {
                        int t=zt[i][tt]^tmp[i];
                        if(t&j) continue;
                        if(t&(k>>1)) continue;
                        if(t&(k<<1)) continue;
                        int j1=j|tmp[i-2],k1=k|tmp[i-1],t1=t|tmp[i];
                        //cout<<tt<<" "<<tmp[i]<<" "<<i<<" "<<t<<" "<<c[t]<<" * ";
                        dp[nw][k1][t1]=max(dp[nw][k1][t1],dp[od][j1][k1]+c[t]);
                    }
                }
            }
        }
        int ans=0,t=(n&1);
        rep(i,0,mm)
        rep(j,0,mm)
        ans=max(ans,dp[t][i][j]);

        printf("%d\n",ans);
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值