今天艾教留了一大堆线段树,表示做不动了,就补补前面的题。QAQ
这个题,我第一次写还是像前面HDU 2167那样写,发现这次影响第 i 行的还用i-2行那样,那以前的方法就行不通了。
找出所有可行的状态,因为每一行最大只有10列,所以一行里最多有4个,那它可行的状态不多(网上大多数说法最多是60个)。用dp[x][i][j]来转移,x表示第x行,i表示第x行的状态,j表示第x-1行的状态。先初始化前两行。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 const int maxn = 105; 6 #define legal(a,b) (a&b) 7 int base[maxn]; 8 int state[maxn]; 9 int solders[maxn]; 10 int dp[maxn][maxn][maxn]; 11 char s[maxn][20]; 12 int num = 0; 13 int main() 14 { 15 int n,m; 16 scanf("%d %d",&n,&m); 17 for(int i=0;i<n;i++) 18 { 19 scanf("%s",s[i]); 20 for(int j=0;j<m;j++) 21 { 22 if(s[i][j]==‘H‘) base[i]+=(1<<j); //记录山地 23 } 24 } 25 int d = 0; 26 for(int i=0;i<(1<<m);i++) //删除一行中相互攻击的点 27 { 28 if(legal(i,i<<1)||legal(i,i<<2)) continue; 29 int k = i; 30 while(k) //记录里面有多少个炮台 31 { 32 solders[num]+=(1&k); 33 k=k>>1; 34 } 35 state[num++] = i; 36 } 37 for(int i=0;i<num;i++) //初始化第0行 38 { 39 if(legal(state[i],base[0])) continue; 40 dp[0][i][0] = solders[i]; 41 } 42 for(int i=0;i<num;i++) //初始化第1行,此为第二行 43 { 44 if(legal(state[i],base[1])) continue; 45 for(int j=0;j<num;j++) //枚举第一行 46 { 47 if(legal(state[j],base[0])) continue; 48 if(legal(state[i],state[j])) continue; 49 dp[1][i][j] = max(dp[1][i][j],dp[0][j][0]+solders[i]); 50 } 51 } 52 53 for(int r=2;r<n;r++) //开始从第二行枚举 54 { 55 for(int i=0;i<num;i++) //枚举r行 56 { 57 if(legal(state[i],base[r])) continue; 58 for(int j=0;j<num;j++) //枚举r-1行 59 { 60 if(legal(state[j],base[r-1])) continue; 61 if(legal(state[j],state[i])) continue; 62 for(int k=0;k<num;k++) //枚举第r-2行 63 { 64 if(legal(state[k],base[r-2])) continue; 65 if(legal(state[k],state[i])) continue; 66 if(legal(state[k],state[j])) continue; 67 dp[r][i][j] = max(dp[r][i][j],dp[r-1][j][k]+solders[i]); 68 } 69 } 70 } 71 } 72 int maxx = 0; 73 for(int i=0;i<num;i++) 74 { 75 for(int j=0;j<num;j++) 76 maxx = max(maxx,dp[n-1][i][j]); 77 } 78 printf("%d\n",maxx); 79 return 0; 80 }
时间: 2024-12-25 11:30:45