分析: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