题目链接:点击打开链接
#include<stdio.h>
#include<string.h>
int dp[110][200][200];// dp[i][j][k] 存第i行 状态为state[j] 上一行状态为state[k] 所能安排士兵的最大值
int num[200];// 存合法状态对应的士兵数
int state[200]; // 存行内不冲突的合法状态
int map[110];// 用二进制按行存图
int limit;// dp上界
int size;// 合法状态数
int n,m;
int get(int x)// 计算状态x 包含1的个数
{
int ans=0;
while(x>0)
{
ans+=x%2;
x=x/2;
}
return ans;
}
void in()// 把行内不冲突的合法状态存到state中
{
int i;
size=0;
limit=1<<10;
for(i=0;i<limit;i++)
{
if((i&(i<<2))==0)
{
num[size]=get(i);
state[size++]=i;
}
}
state[size++]=limit;
}
void init()
{
limit=1<<m;
memset(dp,0,sizeof(dp));
memset(map,0,sizeof(map));
int i,j,x;
for(i=1;i<=n;i++)// 输入图 按行二进制存图
{
for(j=0;j<m;j++)
{
scanf("%d",&x);
map[i]=(map[i]|(x<<j));
}
}
for(i=0;state[i]<limit;i++)// 初始化第一行 第0行初始化为0用来解决n==1的情况
{
if((state[i]&map[1])!=state[i])continue;
dp[1][i][0]=num[i];
}
}
int solve()
{
int i,j,k,l;
for(i=2;i<=n;i++)// 从第二行开始向下dp
{
for(j=0;state[j]<limit;j++)// 枚举第i行的合法状态
{
if((state[j]&map[i])!=state[j])continue;// 状态state[j]必须是map[i]的子集 即状态state[j]的每个士兵必须安排在map[i]中为1的位置
for(k=0;state[k]<limit;k++)
{
if((state[k]&map[i-1])!=state[k])continue;
if((state[k]&(state[j]<<1))!=0)continue;// 判断当前行和上一行的状态是否冲突
if((state[k]&(state[j]>>1))!=0)continue;
for(l=0;state[l]<limit;l++)
{
if((state[l]&map[i-2])!=state[l])continue;
if((state[k]&(state[l]<<1))!=0)continue;// 判断上一行和上上行是否冲突
if((state[k]&(state[l]>>1))!=0)continue;
if((state[l]&state[j])!=0)continue;// 判断当前行和上上行是否冲突
if(dp[i-1][k][l]+num[j]>dp[i][j][k])dp[i][j][k]=dp[i-1][k][l]+num[j];// dp[i][j][k] 存所有与之不冲突的dp[i-1][k][l]+num[j]的最大值
}
}
}
}
int ans=0;
for(i=0;state[i]<limit;i++)//遍历最后一行的所有状态 最大值即为答案
for(j=0;state[j]<limit;j++)
{
if(dp[n][i][j]>ans)
ans=dp[n][i][j];
}
return ans;
}
int main()
{
in();
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0||m==0)
{
printf("0\n");
continue;
}
init();
int ans=solve();
printf("%d\n",ans);
}
return 0;
}