dp[i][j][k]代表前i层,第i层炮兵布局为j,第i-1层炮兵布局为k的最多摆放炮兵数。我们要记录两层的炮兵摆放,而且转移的时候要枚举第i-2层的状态m,使得m与j相容,m与k相容(当然也保证j与k相容),这样才满足无后效性。只要m满足相容的条件,那怎么从第1层到第i-3层摆放炮兵都无所谓了,因为第i层只能打到i-1层和i-2层,与dp[i-1][j][m]这样的方案数怎么达到的没有关系了。
然后每行最多十列,所以每层的状态有1024种。
dp[100][1024][1024]再加上枚举m的线性转移,炸了。
为什么呢?每一层的状态真的有1024种吗?不是这样的,因为每个炮兵还能打左右两格的距离,所以实际上用二进制数每一位表示有没有炮兵来枚举浪费了计算(比如1110000000是不合法的根本没必要枚举)。我们可以dfs一下算出所有横向相容的状态数,发现10列而且都是平原,也只有60种合法状态。所以我们只考虑这60种就可以了
dp[100][60][60]线性转移,再利用相容枚举的时候剪一剪枝就过了。
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 5 char maze[105][15]; 6 int status[70],cnt,n,m; 7 int dp[105][70][70];//dp[i][j][k]第i层布局为j,第i-1层布局为k,前i层的最多摆放炮兵数 8 //dp[i][j][k] = dp[i-1][k][枚举m] 使得m与j和k相容 9 int rong[70][70],rong2[105][70]; 10 int number[70],ans; 11 12 int num(int s){ 13 int count=0; 14 for(int i=1;i<=m;i++){ 15 if( s&(1<<i) ) count++; 16 } 17 return count; 18 } 19 20 void dfs(int col,int last_col,int state){//放到第col列 上一个放炮兵的地方在last_col status是炮兵摆放的状态 21 if(col==m+1){ 22 status[++cnt]=state; 23 number[cnt]=num(state); 24 return; 25 } 26 if(last_col+3<=col) dfs(col+1,col,state+(1<<col) );//这一列放炮兵 27 dfs(col+1,last_col,state);//不放炮兵 28 } 29 30 int check(int s1,int s2){//s1表示的状态是否与s2表示的状态相容 31 if(rong[s1][s2]!=-1) return rong[s1][s2]; 32 int n1=status[s1],n2=status[s2]; 33 for(int i=1;i<=m;i++){//有没有在第i列上放炮兵 34 if( (n1&(1<<i) ) && (n2&(1<<i)) ) return rong[s1][s2]=0; 35 } 36 return rong[s1][s2]=1; 37 } 38 39 int check2(int floor,int s){//能不能在第floor层的土地上放s 40 if(rong2[floor][s]!=-1) return rong2[floor][s]; 41 int n1=status[s]; 42 for(int i=1;i<=m;i++){//有没有在第i列上放炮兵 43 if( (n1&(1<<i) ) && maze[floor][i]=='H' ) return rong2[floor][s]=0; 44 } 45 return rong2[floor][s]=1; 46 } 47 48 int main(){ 49 memset(rong,-1,sizeof(rong)); 50 memset(rong2,-1,sizeof(rong2)); 51 52 cin>>n>>m; 53 dfs(1,-2,0); 54 55 for(int i=1;i<=n;i++) 56 for(int j=1;j<=m;j++) cin>>maze[i][j]; 57 58 //考虑一行中(有m列)全为平地时,能放炮兵的所有合法状态 59 for(int i=1;i<=cnt;i++) { 60 if(check2(1,i)) { 61 dp[1][i][0]=number[i]; 62 ans=max(ans,dp[1][i][0]); 63 } 64 } 65 66 for(int i=1;i<=cnt;i++){ 67 if(check2(2,i) ){ 68 for(int j=1;j<=cnt;j++){//枚举上一层的状态 69 if( check(i,j) && check2(1,j) ) { 70 dp[2][i][j]=dp[1][j][0]+number[i]; 71 ans=max(ans,dp[2][i][j]); 72 } 73 } 74 } 75 } 76 //cout<<"!!!"<<endl; 77 for(int i=3;i<=n;i++){ 78 for(int j=1;j<=cnt;j++){ 79 for(int k=1;k<=cnt;k++){ 80 //dp[i][j][k] == 前i层,第i层摆放为j,第i-1层摆放为k == 最多摆放炮兵的数量 81 for(int m=1;m<=cnt;m++){//枚举第i-2层的摆放情况 82 if( check(m,j) && check(m,k) && check(j,k) && check2(i-2,m) && check2(i,j) && check2(i-1,k) ) { 83 dp[i][j][k]=max( dp[i][j][k] , dp[i-1][k][m]+number[j] ); 84 ans=max(ans,dp[i][j][k]); 85 } 86 } 87 } 88 } 89 } 90 91 cout<<ans; 92 93 return 0; 94 }