题目链接: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;
}