题目链接:https://vjudge.net/problem/POJ-1185
题意:nxm网格,每个格子可能为平原P或山地H。只有平原能放炮兵,炮兵的攻击范围为上下左右各延伸2格的一个十字,一个炮兵不能再其他炮兵的攻击范围内,求最多能摆放多少个炮兵
这题有点麻烦。整个题目分为预处理+状压dp两个部分
首先要两个预处理:一是没有炮兵在别的攻击范围内的状态s,二是状态s在第i行能不能放(因为炮兵只能放在平原P上)
然后是状压dp。设二进制表示下为1是放了炮兵,为0是没有放。这题当前行会被前两行影响,不再像之前做的题目,只有i-1行会影响第i行。如果还是设f[i][s]的话,要考虑f[i-1][s'],f[i-2][s''],我没想出来这个转移该怎么写。一个更好的状态设计是: f[i][s1][s2]表示考虑到第i行,第i行的状态为s1,第i-1行的状态为s2时,前i行最多能放多少个炮兵,则有:
f[i][s1][s2]=max(f[i-1][s2][s3])+__builtin_popcount(s1),此处s1,s2,s3都是合法状态且互相&均为0
#include<iostream> #include<algorithm> using namespace std; int f[110][(1<<10)][(1<<10)],b[(1<<10)],c[15],v[110][(1<<10)]; int n,m,i,j,k,s1,s2,s3,s; char ch[110][12]; int main(){ cin>>n>>m; for (i=1;i<=n;i++) for (j=0;j<m;j++) cin>>ch[i][j]; for (s=0;s<(1<<m);s++){ for (i=0;i<m;i++) c[i]=s&(1<<i); c[m+2]=1; j=0; while (c[j]==0) j++; j++; int flag=0; while (j<m+2){ int num=0; while (c[j]==0) {j++; num++;} if (num<2) flag=1; j++; } b[s]=(flag==0)?1:0; } v[0][0]=1; for (i=1;i<=n;i++) for (s=0;s<(1<<m);s++){ int flag=0; for (j=0;j<m;j++) if (ch[i][j]=='H'&&s&(1<<j)) flag=1; v[i][s]=(flag==0)?1:0; } for (s=0;s<(1<<m);s++) if (b[s]&&v[1][s]) f[1][s][0]=__builtin_popcount(s); for (i=2;i<=n;i++) for (s1=0;s1<(1<<m);s1++) if (b[s1]&&v[i][s1]) for (s2=0;s2<(1<<m);s2++) if (b[s2]&&v[i-1][s2]&&((s1&s2)==0)) for (s3=0;s3<(1<<m);s3++) if (b[s3]&&v[i-2][s3]&&((s2&s3)==0)&&((s1&s3)==0)) f[i][s1][s2]=max(f[i][s1][s2],f[i-1][s2][s3]+__builtin_popcount(s1)); int ans=0; for (s1=0;s1<(1<<m);s1++) for (s2=0;s2<(1<<m);s2++) ans=max(ans,f[n][s1][s2]); cout<<ans<<endl; return 0; }