洛谷 P2704 [NOI2001]炮兵阵地(状压dp)
传送门
解题思路
用dp[h][i][j]表示处理到第h行,这一行状态是i,上一行状态是j的最大数量。
枚举第几行、这一行的状态、上一行的状态、上上行的状态,判断转移即可。
注意要用滚动数组(只会用到上一行、上上行)。
AC代码
1 #include<iostream> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstdio> 5 #include<cstring> 6 using namespace std; 7 int n,m,a[105],dp[3][1100][1100],ans; 8 int work(int x){ 9 int ans=0; 10 while(x>0){ 11 ans+=x&1; 12 x>>=1; 13 } 14 return ans; 15 } 16 int main() 17 { 18 cin>>n>>m; 19 for(int i=1;i<=n;i++){ 20 for(int j=1;j<=m;j++){ 21 char c; 22 cin>>c; 23 a[i]<<=1; 24 if(c=='H') a[i]|=1; 25 } 26 } 27 for(int i=0;i<=(1<<m)-1;i++){ 28 if(i&a[1]) continue; 29 if(i&(i<<1)||i&(i<<2)) continue; 30 dp[1][i][0]=work(i); 31 } 32 for(int i=0;i<=(1<<m)-1;i++){ 33 if(i&a[2]) continue; 34 if(i&(i<<1)||i&(i<<2)) continue; 35 int num=work(i); 36 for(int j=0;j<=(1<<m)-1;j++){ 37 if(j&a[1]) continue; 38 if((j&(j<<1))||(j&(j<<2))||(i&j)) continue; 39 dp[2][i][j]=max(dp[2][i][j],dp[1][j][0]+num); 40 } 41 } 42 for(int h=3;h<=n;h++){ 43 for(int i=0;i<=(1<<m)-1;i++){ 44 if(i&a[h]) continue; 45 if(i&(i<<1)||i&(i<<2)) continue; 46 int num=work(i); 47 for(int j=0;j<=(1<<m)-1;j++){ 48 if(j&a[h-1]) continue; 49 if((j&(j<<1))||(j&(j<<2))||(i&j)) continue; 50 for(int k=0;k<=(1<<m)-1;k++){ 51 if(k&a[h-2]) continue; 52 if((i&k)||(j&k)||(k&(k<<1))||(k&(k<<2))) continue; 53 dp[h%3][i][j]=max(dp[h%3][i][j],dp[(h-1)%3][j][k]+num); 54 } 55 } 56 } 57 } 58 for(int i=0;i<=(1<<m)-1;i++){ 59 for(int j=0;j<=(1<<m)-1;j++){ 60 ans=max(ans,dp[n%3][i][j]); 61 } 62 } 63 cout<<ans; 64 return 0; 65 }