题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4539
题目意思:
给一个矩阵,有些位置不能放士兵。每个士兵可以攻击哈密顿距离恰好为2的所有位置。求最多能放多少个士兵,使得士兵之间不冲突。
解题思路:
状态压缩dp.
因为当前位置与前两行有关,所以把相邻两行作为二维状态,枚举第i-2行即可得到第i行的状态。
记状态dp[][i][j]表示当前行状态为i,前一行状态为j时,最多能放的士兵个数。
dp[][i][j]=Cal(i)+max(dp[][j][k]);
代码:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF 0x1f1f1f1f
#define PI acos(-1.0)
#define ll __int64
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
/*
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
*/
#define Maxn 110
int dp[2][1<<10][1<<10];
int save[Maxn];
int Cal(int st) //统计1的个数
{
int res=0;
while(st)
{
if(st&1)
res++;
st>>=1;
}
return res;
}
int main()
{
int n,m,tmp;
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=n;i++)
{
save[i]=0;
for(int j=0;j<m;j++)
{
scanf("%d",&tmp);
if(!tmp) //0表示不能放,把它作为1考虑 能放作为0考虑
save[i]|=(1<<j);
}
}
memset(dp,0,sizeof(dp));
int lim=1<<m;
for(int i=0;i<lim;i++)
{
if((i&(i<<2))||(i&save[1])) //该状态满足
continue;
dp[1][i][0]=Cal(i); //只用记住一个状态0,表示一个标志
}
int la=1,cur;
for(int i=2;i<=n;i++)
{
cur=la^1;
// memset(dp[cur],0,sizeof(dp[cur]));
for(int j=0;j<lim;j++)
{
if((j&(j<<2))||(j&save[i])) //该状态满足
continue;
for(int k=0;k<lim;k++)
{
if((k&(k<<2))||(k&save[i-1])) //该状态满足当前行的要求
continue;
if(j&(k<<1))
continue;
if(j&(k>>1)) //注意要考虑 左移右移两种情况
continue;
dp[cur][j][k]=Cal(j); //该行1的个数
int Max=0;
for(int p=0;p<lim;p++)
{
if((p&(p<<2))||(p&save[i-2]))
continue;
if((k&(p<<1))||(j&p)) //i-2行与i-1行的关系
continue;
if(k&(p>>1)) //i-2行与i行的关系
continue;
Max=max(Max,dp[la][k][p]);
}
dp[cur][j][k]+=Max;
}
}
la=cur;
}
int ans=0;
for(int i=0;i<lim;i++)
for(int j=0;j<lim;j++) //注意不能构成关系的全为0
ans=max(ans,dp[cur][i][j]);
printf("%d\n",ans);
}
return 0;
}