洛谷 P2704 [NOI2001]炮兵阵地(状压DP)

题目链接: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 }
AC代码

猜你喜欢

转载自www.cnblogs.com/New-ljx/p/12519469.html