题目链接:https://www.luogu.com.cn/problem/P2704
压两行的状压DP。
预处理:将H为1,P为0;在某个点放炮兵为1,不放为0。很显然只有_&_=1时,是不合法的。
首先枚举(1<<m)-1个状态,只将行上没有冲突的状态记录下来,并记录当前行上炮兵的个数,否则会re。
然后处理第一行:如果状态state[i]&mp[1]==0,说明它是合法的,那么dp[1][1][i]=sum[i]。
注意这里,state[1]必然等于0,那么对于第一行,只有0是不会影响第一行状态的,所以它的上一行状态编号为1。
最后每一行处理:枚举上上行、上行和这一行,判断上下三行中是否有炮兵在同一列上,以及这一行上想要放炮兵的位置是否为H,如果有其一,则不合法。
然后暴力转移:dp[i][k][l]=max(dp[i][k][l],dp[i-1][j][k]+sum[l]);
最后ans=max{dp[n][][]},后两维枚举状态。
dp[i][j][k]:[第i行] [第i行状态编号] [第i-1行状态编号]
-->此状态下炮兵总个数。
AC代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 using namespace std; 5 int dp[110][70][70],state[110],sum[110],mp[110],tot; 6 int cul(int x){ 7 int cnt=0; 8 while(x){ 9 if(x&1) cnt++; 10 x>>=1; 11 } 12 return cnt; 13 } 14 int main(){ 15 int n,m; 16 scanf("%d%d",&n,&m); 17 for(int i=1;i<=n;i++) 18 for(int j=0;j<m;j++){//注意从0开始 19 char ch=getchar(); 20 while(ch!='H'&&ch!='P') ch=getchar(); 21 if(ch=='H') mp[i]|=(1<<j);//第j位赋值1 22 } 23 for(int i=0;i<(1<<m);i++) 24 if((i&(i<<1))==0&&(i&(i<<2))==0) state[++tot]=i,sum[tot]=cul(i); 25 for(int i=1;i<=tot;i++) if((state[i]&mp[1])==0) dp[1][1][i]=sum[i];//注意这里的处理 26 for(int i=2;i<=n;i++) 27 for(int j=1;j<=tot;j++) 28 for(int k=1;k<=tot;k++){ 29 if((state[j]&state[k])||dp[i-1][j][k]==0) continue; 30 for(int l=1;l<=tot;l++){ 31 if((state[l]&state[k])||(state[l]&state[j])||(state[l]&mp[i])) continue; 32 dp[i][k][l]=max(dp[i][k][l],dp[i-1][j][k]+sum[l]); 33 } 34 } 35 int ans=0; 36 for(int i=1;i<=tot;i++) 37 for(int j=1;j<=tot;j++) 38 ans=max(dp[n][i][j],ans); 39 printf("%d\n",ans); 40 return 0; 41 }