分析:dp[i][j][k]表示第i行状态为j,i-1行状态为k时的客房士兵的最大值。
曼哈顿距离是指:|x1-x2|+|y1-y2|。
当前行不仅与前一行有关,还和前两行有关,所以开数组的时候还要记录前两行的状态,所以开设三维数组。
每行可压缩为二进制集合,状态dp[i][j][k]为第i行为集合j,第i-1行为集合k,则状态方程dp[i][j][k] = max{dp[i-1][k][r]+cnt[j] | 状态i,j,k要能够共存}(cnt[j]为j在二进制下的1的个数,即士兵数)。第一维可以压缩为2,即两种状态交替进行(这里没有压缩)。
对于每一行可能出现的组合,可预处理出每一种有效状态。
#include<iostream>
using namespace std;
int row[110];
int dp[110][220][220]; //dp[i][j][k]表示第i行状态为j,i-1行状态为k时的士兵最大值
int s[1<<11]; //合法状态
int cnt[1<<11]; //合法状态中1的个数,即可安排的士兵数
int get_cnt(int x)
{
int c=0;
while(x>0)
{
c++;
x=x&(x-1);
}
return c;
}
int sovle(int n,int m)
{
int state; //一行一开始可安排士兵的合法状态总数(处理左右曼哈顿距离等于2的情况)
int i,j,k,l,ans;
state=0;
for(i=0;i<(1<<m);i++) //每行总的状态有2^m
{
if(i&(i<<2)) continue; //若有曼哈顿相距为2的情况则排除
s[state]=i;
cnt[state++]=get_cnt(i);
}
for(i=0;i<state;i++) //第0行
{
if(s[i]&row[0]) continue; //row存的是不能安排士兵的情况
dp[0][i][0]=cnt[i];
}
for(i=1;i<n;i++) //每一行
for(j=0;j<state;j++)
{
if(s[j]&row[i]) continue; //是否能够安排士兵
for(k=0;k<state;k++) //i-1行信息
{
if((s[j]&(s[k]>>1))||(s[j]&(s[k]<<1))) continue;//对角
for(l=0;l<state;l++) //i-2行信息
{
if(s[j]&s[l]) continue; //垂直
dp[i][j][k]=dp[i][j][k]>dp[i-1][k][l]+cnt[j]?dp[i][j][k]:dp[i-1][k][l]+cnt[j];
}
}
}
ans=0;
for(i=0;i<state;i++) //枚举找到最大值
for(j=0;j<state;j++)
ans=ans>dp[n-1][i][j]?ans:dp[n-1][i][j];
return ans;
}
int main()
{
int n,m,i,j,x;
while(scanf("%d%d",&n,&m)==2)
{
memset(row,0,sizeof(row));
memset(dp,0,sizeof(dp));
for(i=0;i<n;i++)
for(j=0;j<m;j++)
{
scanf("%d",&x);
if(!x) row[i]=(row[i]<<1)|1; //一开始不可放士兵的位置设为1,方便后面检测
else
row[i]<<=1; //可放士兵的位置不做处理
}
printf("%d\n",sovle(n,m));
}
return 0;
}
HDU ACM 4539 郑厂长系列故事——排兵布阵->状态压缩DP
原文地址:http://blog.csdn.net/a809146548/article/details/46406055