[POJ 1185] 炮兵陣地


Link:

POJ 1185 傳送門

Solution:

看大家都說是一道狀壓$dp$基礎題,結果我還是卡題了

 

發現決策第$i$行時,要同時考慮$i-1$和$i-2$行,因此狀態中要包含兩個“行”狀態位

但$O(1024^3*100)$的復雜度明顯不行啊,於是我就在這卡住了……

 

其實可以發現僅考慮“行合法”的行狀態與前后行無關,可以預處理,而且只有不到70種!

於是將原來的$1024$種行狀態縮減為已保證“行合法”的$70$中狀態即可

 

這里對每行集體判斷和 [BZOJ 2734] 集合取數  中 求表格上任意兩點不相鄰的點集數 的判斷方式類似

用 !(st[l]&st[j]) 和 !(st[l]&st[k]) 判斷與上兩行的列是否有沖突

同時將原來輸入的'N'、'P',轉換為'1'、'0',判斷位與是否不為0

Code:

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>

using namespace std;

const int MAXN=110,MST=70;
int n,m,dp[MAXN][MST][MST],dat[MAXN],sum[MST],st[MST],tot=0,res=0;

int cal(int x) //計算其中1的個數
{
    int ret=0;
    while(x) ret+=(x&1),x>>=1;
    return ret;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) for(int j=0;j<m;j++)
    {
        char ch=getchar();
        while(ch!='P' && ch!='H') ch=getchar();
        if(ch=='H') dat[i]|=(1<<j);
    }
    for(int i=0;i<(1<<m);i++) //預處理出一行可能的狀態 
        if(!(i&(i<<1)) && !(i&(i<<2))) st[++tot]=i,sum[tot]=cal(i);
    
    for(int i=1;i<=tot;i++)
        if(!(st[i]&dat[1])) dp[1][1][i]=sum[i];
    for(int i=1;i<n;i++)
        for(int j=1;j<=tot;j++)
            for(int k=1;k<=tot;k++)
                if(!(st[j]&st[k]) && dp[i][j][k])
                    for(int l=1;l<=tot;l++) //通過位移和位與集體判斷
                        if(!(st[l]&st[j]) && !(st[l]&st[k]) && !(st[l]&dat[i+1]))
                            dp[i+1][k][l]=max(dp[i+1][k][l],dp[i][j][k]+sum[l]);
    
    for(int i=1;i<=tot;i++) 
        for(int j=1;j<=tot;j++)
            res=max(res,dp[n][i][j]);
    printf("%d",res);
    return 0;
}

Review:

(1)查看有沒有可以事先預處理出的合法的狀態來對總數縮減

 

(2)利用 移位 + 位與 的方式對“行狀態”整體判斷算是常用套路了吧

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM