1. 程式人生 > 實用技巧 >POJ 1185 炮兵陣地 狀壓dp

POJ 1185 炮兵陣地 狀壓dp

題意:

司令部的將軍們打算在N*M的網格地圖上部署他們的炮兵部隊。一個N*M的地圖由N行M列組成,地圖的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下圖。在每一格平原地形上最多可以佈置一支炮兵部隊(山地上不能夠部署炮兵部隊);一支炮兵部隊在地圖上的攻擊範圍如圖中黑色區域所示:

如果在地圖中的灰色所標識的平原上部署一支炮兵部隊,則圖中的黑色的網格表示它能夠攻擊到的區域:沿橫向左右各兩格,沿縱向上下各兩格。圖上其它白色網格均攻擊不到。從圖上可見炮兵的攻擊範圍不受地形的影響。
現在,將軍們規劃如何部署炮兵部隊,在防止誤傷的前提下(保證任何兩支炮兵部隊之間不能互相攻擊,即任何一支炮兵部隊都不在其他支炮兵部隊的攻擊範圍內),在整個地圖區域內最多能夠擺放多少我軍的炮兵部隊。

Input

第一行包含兩個由空格分割開的正整數,分別表示N和M;
接下來的N行,每一行含有連續的M個字元('P'或者'H'),中間沒有空格。按順序表示地圖中每一行的資料。N <= 100;M <= 10。

Output

僅一行,包含一個整數K,表示最多能擺放的炮兵部隊的數量。

Sample Input

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

Sample Output

6

題解:

看一下M的資料範圍只有10,狀壓dp都可以狀壓20位+

然後因為一個位置駐紮的有炮兵,會影響下面兩行,所以我們的dp陣列既要儲存這一行的狀態又要儲存上一行的狀態

因為如果這樣的話,你在dp狀態轉移的時候就可以得到本行上面兩行的狀態

如果一個位置有炮兵,我們就給這個位置用二進位制1來表示,那麼會得到一個M長度的01串,這個串可以變成一個十進位制整數

例如:1001=9、11=3

這就是狀態壓縮,一般見到的都是二進位制狀態壓縮

dp方程:dp[i][j][k]表示第i行的狀態為j,第i-1行狀態為k

狀態轉移方程:dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+num_1[j]);

num_1[j]:第j個狀態中二進位制形勢下1的數量

l:l就是在列舉第i-2行的狀態是什麼

dp過程中把互斥的狀態去掉,例如狀態0110010 和狀態 0000010

這兩個狀態不可以同時出現在i、j、k、l狀態中,因為它們兩個有一個炮兵在同一個位置,而且它們豎向距離小於2,這樣就會攻擊到對方

可以使用&(與,只有1&1才等於1,其他都是0)操作來判斷

另外還需要注意某些位置有山,有山的位置不能放炮兵,也可以壓縮狀態之後使用&操作來判斷

程式碼:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=100+10;
const int N=1<<10;
const int INF=1e9;
int state[N],base[maxn],dp[maxn][N][N],num_1[N];
int main()
{
    int n,m,num=0;
    char s[maxn][maxn];
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;++i)
    {
        scanf("%s",s[i]);
        for(int j=0;j<m;++j)
        {
            if(s[i][j]=='H')
            base[i]+=(1<<j);
        }
    }
    for(int i=0;i<(1<<m);++i)
    {

        if((i&(i<<1) || (i&(i<<2)))) continue;
        state[num]=i;
        int k=i;
        while(k)
        {
            if(k&1) num_1[num]++;
            k>>=1;
        }
        num++;
    }
    for(int i=0;i<num;++i)
    {
        if(state[i]&base[0]) continue;
        dp[0][i][0]=num_1[i];
    }
    for(int i=1;i<n;++i)
    {
        for(int j=0;j<num;++j)
        {
            if(state[j]&base[i]) continue;
            for(int k=0;k<num;++k)
            {
                if(state[k]&base[i-1]) continue;
                if(state[k]&state[j]) continue;
                for(int l=0;l<num;++l)
                {
                    if(i-2>=0 && state[l]&base[i-2]) continue;
                    if(state[j]&state[l]) continue;
                    if(state[k]&state[l]) continue;
                    dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+num_1[j]);

                }
            }
        }
    }
    int maxx=0;
    for(int i=0;i<num;++i)
    {
        for(int j=0;j<num;++j)
        {
            maxx=max(maxx,dp[n-1][i][j]);
        }
    }
    printf("%d\n",maxx);
    return 0;
}