1. 程式人生 > >狀壓dp NOI 2001 炮兵陣地

狀壓dp NOI 2001 炮兵陣地

題意:
給你一個 n m n∗m 的網格,每個各自最多放一個炮兵,每一個格子有地形,平原可以放,山地不能放,並且一個炮兵的上下左右兩個內不能放炮兵,問最多放多少炮兵。 n

100 m 10 n≤100;m≤10

可以發現m非常小,考慮狀壓dp,這道題與常規狀壓dp的不同點是這道題的每一行狀態在轉移時要看之前兩行的狀態。

首先我們先將每一行哪些格子可以放用二進位制表示出來, g

[ i ] g[i] 表示第 i i 行地形情況的二進位制狀態。然後處理出一行內所有合法的情況。我們設 d
p [ i ] [ j ] [ k ] dp[i][j][k]
表示第 i i 行,當前狀態的 j j ,上一行的狀態是 k k 最多放多少炮兵,我們按順序列舉當前行,上一行,和上兩行的狀態,要保證當前行的狀態不與當前行的地形狀態衝突,當前行的狀態不與上一行和上兩行的狀態衝突,上一行和上兩行的狀態不衝突。

所以 d p [ i ] [ j ] [ k ] = m a x ( d p [ i ] [ j ] [ k ] d p [ i 1 ] [ k ] [ q ] + s [ j ] ) dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][q]+s[j]) s [ j ] s[j] j j 狀態下 1 1 的個數,即為該狀態下可以放的炮兵的個數。最後答案就是 m a x max { d p [ n ] [ i ] [ j ] dp[n][i][j] }, i , j i,j 為最後一行和倒數第二行的所有狀態。

#include<bits/stdc++.h>
using namespace std;
int n,m,g[1000000],num,res[1000000],ans,dp[105][1024][1024],s[1000000];
char st[1001000];
//g陣列為第i行山地平原情況的狀壓,0為平原,1為山地 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",st+1);
        for(int j=1;j<=m;++j)
        {
            if(st[j]=='H')
                g[i]+=1<<(j-1);
        }		
    }	
    for(int i=0;i<(1<<m);++i) 
    {
        if(!(i&(i>>1))&&!(i&(i>>2))&&!(i&(i<<1))&&!(i&(i<<2)))
        {
            res[++num]=i;//可行狀態 
            int w=i;
            while(w)
            {
                if(w%2==1)
                    s[num]++;//該狀態下1的個數(放的炮的個數) 
                w/=2;
            }
            if(!(i&(g[1])))//初始化第一行狀態 
                dp[1][num][0]=s[num]; 
        }	
    }
    
    for(int i=1;i<=num;++i)//列舉第一行的狀態 
        for(int j=1;j<=num;++j)//列舉第二行的狀態 
            if(!(res[i]&res[j])&&!(g[2]&res[j]))//判斷是否與地形和第一行衝突
                dp[2][j][i]=max(dp[2][j][i],dp[1][i][0]+s[j]);
                
    for(int i=3;i<=n;++i)
        for(int j=1;j<=num;++j)//當前行的狀態 
            if(!(g[i]&res[j]))//當前行的狀態不與地形衝突 
                for(int k=1;k<=num;++k)//前一行的狀態 
                    if(!(res[k]&res[j]))//前一行的狀態與當前行不衝突 
                        for(int q=1;q<=num;++q)//前兩行的狀態 
                            if(!(res[q]&res[k])&&!(res[q]&res[j]))//前兩行的狀態不與前一行的狀態和當前行的狀態衝突 
                                dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][q]+s[j]);
                                
    for(int i=1;i<=num;++i)
        for(int j=1;j<=num;++j)
            ans=max(ans,dp[n][i][j]);
            
    cout<<ans;					
    return 0;
}