#\(\color{red}{\mathcal{Description}}\)
司令部的将军们打算在\(N \times M\)的网格地图上部署他们的炮兵部队。一个\(N \times M\)的地图由\(N\)行\(M\)列组成,地图的每一格可能是山地(用“\(H\)” 表示),也可能是平原(用“\(P\)”表示)。并且事实上山地不能部署。 现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
#\(\color{red}{\mathcal{Solution}}\)
对于这个题而言,我们考虑状压\(DP\),但是状压的时候我们会发现,他的状态是跟前两行都有关系的。所以我们不妨考虑其状态为\(dp_{i,j,k}\),及前\(i\)行、第\(i\)行状态为\(j\)、第\(i - 1\)行状态为\(k\)的部队数量。那么很显然的状态转移方程是\[dp_{i,j,k} = max \{ dp_{i - 1, k, l} \} +getlen(j)\]
\(emmm\)我才不会告诉你一开始我把这个方程里面最后加的\(getlen(j)\)写成了\(+1\)
\(Obviously\)第一维是可以滚掉的……然后尽量还是把合法状态预处理一下比较好不预处理就会十分恶心并且我根本调不出来\(ORZ\)
#include#include #include #define MAXN 1096using namespace std ;int tot, s[MAXN], getnum[MAXN ];int ans, i, j, k, l, d, Mx ; char c ;int N, M, List[MAXN], dp[2][MAXN][MAXN] ;inline int getL(int x){ int ret = 0 ; while(x){ if(x & 1) ret ++ ; x >>= 1 ; } return ret ;}int main(){ cin >> N >> M ; for(i = 1; i <= N; i ++) for(j = 1; j <= M; j ++){ cin >> c ; if(c == 'H') List[i] += 1 << j - 1 ; } Mx = (1 << M) - 1 ; for(i = 0; i <= Mx; i ++){ if((i & (i >> 1)) || (i & (i >> 2)) || (i & (i << 1)) || (i & (i << 2))) continue ; ++ tot, s[tot] = i ; getnum[tot] = getL(i) ; if(List[1] & i) continue ; dp[1][0][tot] += getnum[tot] ; } for(i = 1; i <= tot; i ++) for(j = 1; j <= tot; j ++){ if((s[i] & s[j]) || (s[i] & List[1]) || (s[j] & List[2])) continue ; dp[0][i][j] = max(dp[0][i][j], dp[1][0][i] + getnum[j]) ; } for(d = 1, i = 3; i <= N; i ++, d ^= 1){ memset(dp[d], 0, sizeof(dp[d])) ; for(j = 1; j <= tot; j ++){ if(s[j] & List[i]) continue ; for(k = 1; k <= tot; k ++){ if((s[j] & s[k]) || (s[k] & List[i - 1])) continue ; for(l = 1; l <= tot; l ++) if((s[l] & s[k]) || (s[l] & s[j]) || (s[l] & List[i - 2])) continue ; else dp[d][k][j] = max(dp[d][k][j], dp[d ^ 1][l][k] + getnum[j]) ; } } } for(i = 1; i <= tot; i ++) for(j = 1; j <= tot; j ++) ans = max(ans, dp[N & 1][i][j]) ; cout << ans ;}