1. 程式人生 > >洛谷P2704 [NOI2001]炮兵陣地【狀壓DP】

洛谷P2704 [NOI2001]炮兵陣地【狀壓DP】

時空限制 1000ms / 128MB

題目描述

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

在這裡插入圖片描述

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

輸入格式:

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

輸出格式:

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

題目分析

dp[i][j][k]dp[i][j][k]表示考慮ii,且ii行狀態為jji1i-1行狀態為kk能放置的最多數量 dp方程dp[i][j][k]=max(dp[i1][k][l]+sum(j))dp[i][j][k]=max(dp[i-1][k][l]+sum(j))

][j][k]=max(dp[i1][k][l]+sum(j))(狀態j,k,lj,k,l都合法,sum(j)sum(j)jj狀態的炮兵數)

如果直接列舉狀態更新複雜度為O(n(2m)3)O(n*(2^m)^3),顯然無法承受 於是考慮先把每一行只考慮單行不考慮地形的所有和合法狀態枚舉出來(即只考慮一行內不互相攻擊) 可以通過計算得出一行內這樣的合法狀態數不超過70 於是列舉狀態的時間複雜度就大大降低

最後答案為ans=max(dp[n][i][j])ans=max(dp[n][i][j])

#include
<iostream>
#include<cmath> #include<algorithm> #include<queue> #include<cstring> #include<cstdio> using namespace std; #define lowbit(x) ((x)&(-x)) int read() { int f=1,x=0; char ss=getchar(); while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();} while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();} return f*x; } const int N=110; const int M=13; int n,m; int rem[N]; int st[1<<M],num[1<<M],cnt; int dp[N][M*10][M*10],ans; char ss[M]; int check(int x) { if(((x<<1)&x)||((x>>1)&x)) return 0; if(((x<<2)&x)||((x>>2)&x)) return 0; return 1; } int qsum(int x) { int res=0; for(int i=x;i>0;i-=lowbit(i)) res++;//用lowbit計算1的個數 //for(int i=1;i<=15;++i) //if(x&(1<<i-1)) res++; return res; } int main() { n=read();m=read(); for(int i=1;i<=n;++i) { scanf("%s",&ss); for(int j=0;j<m;++j) if(ss[j]=='H') rem[i]|=1<<j; } for(int i=0;i<=(1<<m)-1;++i)//列舉一行內不考慮地形的合法狀態 if(check(i)){ st[++cnt]=i; num[cnt]=qsum(i); } memset(dp,-1,sizeof(dp)); for(int i=1;i<=cnt;++i) if(!(st[i]&rem[1])) dp[1][i][1]=num[i];//初始化第1行狀態為i,上一行狀態為全0 for(int i=2;i<=n;++i) for(int j=1;j<=cnt;++j)//第i行的狀態 if(!(st[j]&rem[i])) for(int k=1;k<=cnt;++k)//第i-1行的狀態 if(!(st[k]&rem[i-1])&&!(st[j]&st[k])) for(int l=1;l<=cnt;++l)//第i-2行的狀態 if(!(st[j]&st[l])&&!(st[l]&st[k])&&!(rem[i-2]&st[l])&&dp[i-1][k][l]!=-1) dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+num[j]); for(int i=1;i<=cnt;++i) for(int j=1;j<=cnt;++j) ans=max(ans,dp[n][i][j]); printf("%d",ans); return 0; }