Hdu 4539 郑厂长系列故事——排兵布阵 状态压缩

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4539

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

解题思路:和上一道炮兵阵地  一样的处理,只是判状态注意一下;

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[105][180][180]; // dp[i][j][k]  表示第i行第j个状态,第i-1行第k个状态的最大值
int sta[180];  //合法的状态
int cot[180];  //每个合法状态的炮兵数
int map[1<<11];  //山地的位置
int n,m;
void init()
{
    memset(dp,-1,sizeof(dp));  //此处 初始化为-1,下面 的状态转移 可以剪枝,特别注意
    memset(sta,0,sizeof(sta));
    memset(cot,0,sizeof(cot));
    memset(map,0,sizeof(map));
}
void solve()
{
    int num=(1<<m),ct=0;

    for(int i=0;i<num;i++)  //筛选每行的合法状态
        if( (i&(i<<2))==0  )
            sta[ct++]=i;

    for(int i=0;i<ct;i++)
    {
        int cnt=0;
        for(int j=0;j<m;j++)
            if( sta[i]&(1<<j) )  //判断每个合法状态能放的炮兵数
                    cnt++;

        if( (map[1]&sta[i]) == 0 )//初始化  第一行状态
                dp[1][i][0]=cnt;

        cot[i]=cnt;
    }

    for(int i=2; i<=n; i++)  //枚举行数
        for(int k1=0; k1<ct; k1++)  //枚举上一行状态
            for(int k2=0; k2<ct; k2++)  //枚举上两行状态
                if(dp[i-1][k1][k2]>=0)   //可能出现0状态,所以要>=0
                for(int j=0; j<ct; j++)   //枚举当前行状态
                    if( (sta[j]&(sta[k1]<<1))==0 && (sta[j]&(sta[k1]>>1))==0 && (sta[j]&sta[k2])==0 && (map[i]&sta[j])==0 )
                        dp[i][j][k1]=max( dp[i][j][k1],dp[i-1][k1][k2]+cot[j]);
    int ans=0;
    for(int i=0; i<ct; i++)
        for(int j=0; j<ct; j++)
            ans=max(dp[n][i][j],ans);
    printf("%d\n",ans);
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(~scanf("%d%d",&n,&m))
    {
        init();
        int a;
        for(int i=1; i<=n; i++)
            for(int j=0; j<m; j++)
            {
                scanf("%d",&a);
                if(a==0) map[i]=(map[i]<<1)+1;
                else map[i]=(map[i]<<1)+0;
            }
        solve();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值