【状态压缩DP】 HDU 4539 郑厂长系列故事——排兵布阵

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

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


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
 

Source
 

Recommend
liuyiding


因为是中文题,这里就不给出题意解释了。

由题意我们可知对于我们任意一个士兵,在其曼哈顿距离为2的点上我们不能放置新的士兵。通过观察我们发现士兵的位置是斜向影响的,即横纵坐标值的和值为奇数的相互影响,为偶数的相互影响,而奇数和偶数之间没有影响关系。

那么我们可以将图像处理成这样(以样例为例)

                                               0
                                            0 0
                                         0 0 0
                                      0 0 0 0
                                   0 0 0 0 0
                                0 0 0 0 0 0                                                  0 0 0 0 0 0
                                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
                                0 0 0 0 0 0                                                  0 0 1 0 0 0
                                0 0 0 0 0 0                                                  0 0 0 1 0 0
                                0 0 0 0 0                                                     0 0 0 0 0 0
                                0 0 0 0                                                        0 0 0 0 0 0
                                0 0 0                                                           0 0 0 0 0 0
                                0 0                                                              0 0 0 0 0 0
                                0                                                                 0 0 0 0 0 0


其中然后我们的任务就是分别对红色和蓝色区域做状态压缩DP就行了,其中每一行的不能有两个连续的士兵,每一个士兵对于下一行的影响是当前位置以及之后两位的正下方即(P|P<<1|P<<2)。

代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>

using namespace std;

int n,m;
int mp[125][11];
int mp2[125];
int dp[125][1100];

int able[1024][1024];
int val[1024];

bool judge(int x)
{
    int a=x&1;
    x>>=1;
    while(x)
    {
        int r=x&1;
        if(a && r) return false;
        a=r;
        x>>=1;
    }
    return true;
}

void init()
{
    for(int i=0;i<1024;i++)
    {
        int x=i,ans=0;
        while(x)
        {
            if(x&1) ans++;
            x>>=1;
        }
        val[i]=ans;
    }

    for(int i=0;i<1024;i++)
    {
        for(int j=0;j<1024;j++)
        {
            if(judge(j) && (i&j)==j) able[i][j]=1;
        }
    }
}

void solve(int x,int now)
{
    //cout<<x<<" "<<now<<endl;

    if(x>n+m) return;
    if(dp[x][now]>=0) return;

    dp[x][now]=0;

    for(int i=0;i<(1<<m);i++)
    {
        if(!able[now][i]) continue;

        //cout<<(i | (i<<1) | (i<<2))%(1<<m)<<endl;

        int neww=~((~mp2[x+2]) | ((i | (i<<1) | (i<<2))%(1<<m)));
        solve(x+2,neww);
        dp[x][now]=max(dp[x+2][neww]+val[i],dp[x][now]);
    }
}

int main()
{
    init();
    while(~scanf("%d%d",&n,&m))
    {
        if(n*m==0)
        {
            printf("0\n");
            continue;
        }

        memset(mp,0,sizeof mp);
        memset(mp2,0,sizeof mp2);
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                scanf("%d",&mp[i+10][j]);
            }
        }

        for(int i=10;i<n+m-1+10;i++)
        {
            for(int j=m-1;j>=0;j--)
            {
                mp2[i-10]<<=1;
                mp2[i-10]|=mp[i-j][j];
            }
        }
        //cout<<mp2[6]<<endl;

        memset(dp,-1,sizeof dp);
        solve(0,mp2[0]);
        solve(1,mp2[1]);

        printf("%d\n",dp[0][mp2[0]]+dp[1][mp2[1]]);
    }
    return 0;
}

/*
4 4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
*/






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值