题目链接: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;
}