poj 1185 炮兵阵地 三进制状态压缩,DFS,滚动数组
我们把 放置在 第 I 行的炮兵 称为 I 行炮兵, 通过观察我们可以得出, 第I行炮兵只受 I-1 行,I-2行的 放置情况影响.
对于第Y列,有三种情况:
P[x] = 0, i-1 行 空 i-2 行 空
P[x] = 1, i-1 行 空, i-2 行放置了炮兵
P[x] = 2, i-1 行 放置炮兵 (因为 此行放置了炮兵, 哪怕 I-2行 空,对于 I行而言,也毫无意义)
因为这题与 1038不同, 若当前位置为平原, 只影响当前层,而对下一层无影响,所以我们不能将 平原和高山 情况合并到状态中, 其实直接单点考虑也没事.
因为一共M列,每一列都由一个 三进制数表示, 则(I-1,I-2)的放置情况可以通过 一串三进制的序列表示,为 P1,P2,...,Pm,称为放置序列
我们可以通过 (I,I-1)的放置情况得出 I+1 层放置方案, 而且要更新得到 (I+1,I)放置序列
其实去掉 I-1 层的影响就可以了, 这也是为何 将 I行 放置炮兵时,P[x] = 2, 因为其还能向下影响 两行,随着行数增多,影响逐渐变小
(I+1,I)层 放置序列 Q[y] = P[y] > 0 ? P[y]-1 : 0
定义状态 dp(I,J),表示 第I行, 状态J 表示 (I,I-1)层的放置情况 下最大 炮兵部署数量
方程转移有三种情况:
一,当前层不放置任何炮兵,此时 dp(i+1,k) = dp(i,j)
二,对于当前列 y, 可以不放置,考虑y+1开始放置
三,若 P[y] = 0, 则放置炮兵并设定状态 k = k + 2*3^(y-1) , 继续考虑 y+3列
解题代码
#include<stdio.h> #include<stdlib.h> #include<string.h> #define MAX(a,b) (a)>(b)?(a):(b) int dp[2][60000], A[11], P[11], Q[11]; char mp[110][15]; int n, m, mask; int GetState(int f[]) { int k = 0; for(int i = 1; i <= m; i++) k += f[i]*A[i-1]; return k; } int GetBack( int x, int f[] ) { for(int i = 1; i <= m; i++) { f[i] = x%3; x /= 3; } } void input() { A[0] = 1; for(int i = 1; i <= 10; i++) A[i] = 3*A[i-1]; for(int i = 1; i <= n; i++) scanf("%s", mp[i]+1 ); } void dfs( int i, int x, int num ) { if( x > m ) return; int cur = (i+1)&1, nxt = i&1, k = GetState(Q); //当前层 可以不放置炮兵 dp[nxt][k] = MAX( dp[nxt][k], dp[cur][GetState(P)] ); //情况一,当前列x不放,考虑x+1列 if( x < m ) dfs( i, x+1, num ); //情况二,若当前位置可以放置,且(i+1,x)位置为平原 if( (mp[i][x]=='P') && (P[x]==0) && (Q[x]==0)) { // printf("x = %d,y = %d, ch = %c\n", i+1,x,mp[i+1][x]); Q[x] = 2; int kk = GetState(Q); dp[nxt][kk] = MAX( dp[nxt][kk], num+1 ); dfs( i, x+3, num+1 ); Q[x] = 0; } } void solve() { memset( P, 0, sizeof(P) ); memset( dp, 0xff, sizeof(dp) ); dp[0][0] = 0; mask = A[m]; for(int i = 1; i <= n; i++) { int cur = (i+1)&1, nxt = i&1; memset( dp[nxt], 0xff, sizeof(dp[nxt]) ); for(int j = 0; j < mask; j++) { if( dp[cur][j] == -1 ) continue; GetBack( j, P ); for(int x = 1; x <= m; x++) Q[x] =( (P[x] > 0) ? (P[x]-1) : 0 ); dfs( i, 1, dp[cur][j] ); } } int ans = 0; for(int i = 0; i < mask; i++) ans = MAX( ans, MAX( dp[0][i],dp[1][i] ) ); printf("%d\n", ans ); } int main() { while( scanf("%d%d",&n,&m) != EOF) { input(); solve(); } return 0; }