狀壓dp NOI 2001 炮兵陣地
阿新 • • 發佈:2018-11-08
題意:
給你一個
的網格,每個各自最多放一個炮兵,每一個格子有地形,平原可以放,山地不能放,並且一個炮兵的上下左右兩個內不能放炮兵,問最多放多少炮兵。
。
可以發現m非常小,考慮狀壓dp,這道題與常規狀壓dp的不同點是這道題的每一行狀態在轉移時要看之前兩行的狀態。
首先我們先將每一行哪些格子可以放用二進位制表示出來, 表示第 行地形情況的二進位制狀態。然後處理出一行內所有合法的情況。我們設 表示第 行,當前狀態的 ,上一行的狀態是 最多放多少炮兵,我們按順序列舉當前行,上一行,和上兩行的狀態,要保證當前行的狀態不與當前行的地形狀態衝突,當前行的狀態不與上一行和上兩行的狀態衝突,上一行和上兩行的狀態不衝突。
所以 , 為 狀態下 的個數,即為該狀態下可以放的炮兵的個數。最後答案就是 { }, 為最後一行和倒數第二行的所有狀態。
#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;
}