POJ 1185 (狀態壓縮DP)

2021-06-21 22:06:16 字數 1668 閱讀 4427

中文題目,題意就不說了。

不得不說這是一道十分經典的狀態壓縮dp的題目。

思路: 通過分析可以發現,第i行的格仔能不能放大炮僅與第i-1和i-2行的放法有關,而與前面的放法無關,因此,如果我們知道了i-1行和i-2放的狀態,那麼,我們就可以推出第i行的可行的放法狀態。因此可以看出i行的狀態由它上面兩行決定。

設dp[i][j][k]表示前i行,第i行用第j個狀態放,第i-1行用第k個狀態放的最大大炮的個數,那麼狀態轉移方程就是:dp[i][j][k] = max(dp[i][j][k],dp[i-1][k][t]+num1[j]),其中num1[j]表示第i行用第j個狀態放時,該行放的大炮的個數。通過列舉所有可行的狀態則可以求出前n行能放的大炮的最大個數。注意,上述狀態轉移方程中的j,k,t,分別表示第i,i-1,i-2行的狀態,他們不能發生衝突。

上面講了一下具體的思想和轉移方程,下面說說狀態壓縮,即:我們怎樣表示每行放的大炮的狀態,用什麼表示?

可以看到,題目中列數最大為10,這不得不誘導我們往二進位制位上面考慮。乙個int型數長度位32位 > 10,那麼我們可以用乙個整數的二進位制位中第i位表示該某行第i列放與不放,為1表示已放置大炮,為0表示未位置大炮,乙個整數完全可以表示出所有狀態事實上由於一行中的不同列放置大炮之間的距離必須不小於2,因此10列最多60個可行狀態,而不是2^10個狀態,也就是說dp陣列中的j與k最大為60,可以另開乙個陣列來記錄某行可行狀態(跟離散化思想挺像的),陣列中下標為j的數對應第j個狀態,這樣不至於導致dp陣列開得太大超記憶體,也優化了時間。同時再記錄下在狀態j下,該狀態中含有1的個數,即表示放的大炮的個數。

最後再說下位運算技巧:

1.給出狀態j(對應的數為x)怎樣得到該狀態中1的個數?可以這麼幹:x &= (x-1),ret++; 其中x&(x-1)表示每次刪去二進位制中最後面那個1.

2.怎樣知道某個數x表示的狀態是否可行? 看x & (x << 1) 和  x & (x << 2)是否都是0即可。

其他部分用的位運算都比較容易理解,比如置位x |= (1 << bit),檢驗狀態是否衝突x & y 等等,就不一一說了。

#include#include#include#include#includeusing namespace std;

int n, m, top;

int dp[111][70][70], map[111];

int status[70], num1[70];

int calnum1(int x)

return ret;

}bool canplace(int x)

void init()

}}int main()

init();

for(int i = 1;i <= top;i ++)

for(int i = 2;i <= n;i ++)}}

}int ans = 0;

for(int i = 1;i <= top;i ++)

for(int j = 1;j <= top;j ++)

ans = max(ans, dp[n][i][j]);

printf("%d\n", ans);

}return 0;

}

poj 1185 狀態壓縮DP

這題開始直接狀態壓縮每行 2 m 然後就tle了。最後看了下別人的發現其實每行的狀態沒有這麼多,因為相鄰兩個大炮的距離不能小於2 所以當m為10 的時候每行的狀態只有60種,每次在得到m的時候,直接搜出這些狀態儲存起來,然後就可以了。ac 如下 include include include inc...

poj1185 狀態壓縮DP

狀態壓縮學習 tju 周偉的 狀態壓縮 第乙個狀態壓縮dp,留 以後好看 1 include2 include 3 include4 define n 101 5 define m 11 6 define s 66 7 define max a,b a b?a b89 intstate n s 10...

poj1185 狀態壓縮的dp

noi炮兵陣地,經典題!題意 略 解答 我一開始的思路是基於三進製的,0表示這個方格不受控制,1表示這個方格收到距離為1的方格的控制,2表示這個方格收到距離為2的方格的控制。後來發現有點煩!所以換了乙個。令f i j k 表示第i行,i 1行狀態為k,i 2行的狀態為j。j,k均為01串,1表示有炮...