poj 1185 炮兵陣地 狀壓dp

題目:點擊打開鏈接

題意:如圖,大炮可以放在p處,黑色區域是可以攻擊的範圍,問怎麼放大炮可以不會誤傷,求出最大數?

分析:

和上一篇poj3254一樣,這是這題多加了一個判斷,就是還要考慮上上行的情況,其實跟上一題差不多,具體參考代碼。

【狀態表示】dp[i][j][k] 表示第i行狀態爲k,第i-1狀態爲j時的最大炮兵個數。 

【狀態轉移方程】dp[i][k][t] =max(dp[i][k][t],dp[i-1][j][k]+num[t]); num[t]爲t狀態中1的個數

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
int cur[110],st[60],f[110][60][60],num1[60];
char s[110][12];
int n,m,num;
int count1(int x) 
{
    int cnt=0;
    while(x)
    {
        cnt++;
        x&=(x-1);
    }
    return cnt;
}
int main()
{
    //freopen("f.txt","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%s",s[i]+1);
    mem(f,-1);
    mem(cur,0);
    mem(num1,0);
    mem(st,0);
    num=0;
    for(int i=0;i<(1<<m);i++){
        if(i&(i<<1)||i&(i<<2))continue;
        else st[++num]=i;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(s[i][j]=='H')cur[i]+=1<<(j-1);
        }
    }
    for(int i=1;i<=num;i++){
        num1[i]=count1(st[i]);
        if(st[i]&cur[1])continue; 
        else f[1][1][i]=num1[i];//預處理第1行的狀態
    }
    for(int i=2;i<=n;i++){
        for(int j=1;j<=num;j++){ //枚舉第i行的可行狀態
            if(st[j]&cur[i])continue;
            for(int k=1;k<=num;k++){  //枚舉第i-1行的可行狀態
                if(st[j]&st[k]||st[k]&cur[i-1])continue;
                for(int z=1;z<=num;z++){   //枚舉第i-2行的可行狀態
                    if(st[z]&st[j])continue;
                    if(f[i-1][z][k]!=-1){
                        f[i][k][j]=max(f[i][k][j],f[i-1][z][k]+num1[j]);
                    }
                }
            }
        }
    }
    int ans=0;
    for(int j=1;j<=num;j++){
        for(int k=1;k<=num;k++)
            ans=max(ans,f[n][j][k]);
    }
    cout<<ans<<endl;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章