這個題目和上一個種玉米的是一個類型,都是狀態dp,用二進制位來表示當前的一個狀態值,只不過比上一個稍微復雜了一點,需要用三維的數組來保存當前state。
題目:在一個N*M的矩陣上布置炮兵部隊,只有平原可以布置,然后每個炮兵部隊都有一個攻擊范圍,它能夠攻擊到的區域:沿橫向左右各兩格,沿縱向上下各兩格。
問:如何部署炮兵部隊,在防止誤傷的前提下(保證任何兩支炮兵部隊之間不能互相攻擊,即任何一支炮兵部隊都不在其他支炮兵部隊的攻擊范圍內),在整個地圖區
域內最多能夠擺放多少我軍的炮兵部隊?
由於是求的最多能放置的炮兵個數,就是求某一個狀態下,它對應的炮兵個數最多,所以就想到dp方程肯定是那種dp[i+1]=max{dp[i-1]..}的形式,又考慮到每一行
的狀態只和前兩行有關系,所以考慮用dp來做,下面考慮如何用二進制位來表示一個狀態及轉移方程。
對於原始的矩陣,我們用1來表示可以放置炮兵,即對應圖中的P,這樣每一行都有一個可以放置炮兵的狀態,存到rstate[N]中,用來check該行的狀態是否合法。
由於當前行和前兩行有關系,所以得用3維矩陣來保存一個狀態下最多的炮兵個數,用dp[i][curst][prest]表示當前第i行狀態對curst,前一行狀態為prest的最大炮兵數。
轉移方程為dp[i][curst][prest]=max{dp[i-1][prest][preprest]},這樣求到最后一行之后,答案就是最后一行所有狀態中最大的那個。程序初始化的時候需要對第一行
進行預處理,設置dp[0][st][0]=st合法&st中1的個數。這樣進行下面的計算的時候,由於0狀態肯定是和所有狀態兼容的,所以就不會影響計算結果。
代碼如下:

#include <iostream> #include <stdio.h> using namespace std; const int N = 101; const int M=11; const int MAXS = 61; char matrix[N][M]; int dp[N][MAXS][MAXS]; int vst[MAXS]; int num[MAXS]; int rstate[N]; int vnum = 0, n = 0, m = 0; int getnum(int i) { int ret = 0; while(i) { i &= i - 1; ++ret; } return ret; } void initstate() { vnum = 0; for(int i = 0; i < (1<<m); ++i) { if(!(i&(i<<1)) && !(i&(i<<2))) { vst[vnum] = i; num[vnum++] = getnum(i); } } } int main() { int i = 0, j = 0, k = 0, t = 0; while(EOF != scanf("%d%d", &n, &m)) { for(i = 0; i < n; ++i) { rstate[i] = 0; scanf("%s", matrix[i]); for(j = 0; j < m; ++j) { if('P' == matrix[i][j]) { rstate[i] += (1<<(m-j-1)); } } } for(i = 0; i < n; ++i) for(j = 0; j < MAXS; ++j) for(k = 0; k < MAXS; ++k) dp[i][j][k] = -1; initstate(); for(i = 0; i < vnum; ++i) if(!(vst[i]&(~rstate[0]))) dp[0][i][0] = num[i]; //dp[i][j][k] = max{dp[i][j][k], dp[i-1[k][t]]+num[j]} for(i = 1; i < n; ++i) { for(j = 0; j < vnum; ++j)// state of row i { if((~rstate[i])&vst[j]) continue; for(k = 0; k < vnum; ++k)//state of row i-1 { if((~rstate[i-1])&vst[k]) continue; if(vst[j]&vst[k]) continue; for(t = 0; t < vnum; ++t)//state of row i-2 { if(vst[j]&vst[t]) continue; if(-1 == dp[i-1][k][t]) continue; dp[i][j][k]=(dp[i][j][k]>=dp[i-1][k][t]+num[j])?dp[i][j][k]:dp[i-1][k][t]+num[j]; } } } } int ret = 0; for(i = 0; i < vnum; ++i) { for(j = 0; j < vnum; ++j) { if(dp[n-1][i][j]>ret) ret = dp[n-1][i][j]; } } printf("%d\n", ret); } return 0; }