洛谷傳送門php
JDOJ1105傳送門ios
司令部的將軍們打算在NM的網格地圖上部署他們的炮兵部隊。一個NM的地圖由N行M列組成,地圖的每一格多是山地(用「H」 表示), 也多是平原(用「P」表示),以下圖。在每一格平原地形上最多能夠佈置一支炮兵部隊(山地上不可以部署炮兵部隊); 一支炮兵部隊在地圖上的攻擊範圍如圖中黑色區域所示:優化
若是在地圖中的灰色所標識的平原上部署一支炮兵部隊,則圖中的黑色的網格表示它可以攻擊到的區域:沿橫向左右各兩格,沿縱向上下各兩格。 圖上其它白色網格均攻擊不到。從圖上可見炮兵的攻擊範圍不受地形的影響。 spa
如今,將軍們規劃如何部署炮兵部隊,在防止誤傷的前提下(保證任何兩支炮兵部隊之間不能互相攻擊,即任何一支炮兵部隊都不在其餘支炮兵部隊的攻擊範圍內) ,在整個地圖區域內最多可以擺放多少我軍的炮兵部隊。設計
第一行包含兩個由空格分割開的正整數,分別表示N和M; 接下來的N行,每一行含有連續的M個字符(‘P’或者‘H’),中間沒有空格。按順序表示地圖中每一行的數據。 N≤100;M≤10。code
僅在第一行包含一個整數K,表示最多能擺放的炮兵部隊的數量。ip
5 4 PHPP PPHH PPPP PHPP PHHPci
6部署
衆所周知,這是一道狀壓\(DP\)的題目。
衆所周知,設計狀態的時候要考慮邊界。
不能死腦筋,一作狀壓就想以前的題只設計了前面一個狀態來轉移,這道題不同。爲何呢?就像斐波那契數列的遞推式:\(f[i]=f[i-1]+f[i-2]\)。這個轉移是和前兩個數有關的。同理,由於這臺意大利炮能打前面兩格,因此設計狀態的時候天然就要把兩個狀態壓進去:如今的和前一個。
那麼由此得出狀態:
\(dp[i][j][k]\)表示行數爲\(i\)、當前狀態爲\(j\)、上一狀態爲\(k\)時的最大炮數。
這裏狀態0/1表示的不是能不能被炮攻擊到,而只是單純的有沒有炮(若是按前面的設置狀態的話無法統計答案)。
而後咱們考慮轉移的條件。有兩個條件限制了咱們轉移:第一種是原本就不能放大炮。即山地的狀況。第二種是由於容易被其餘大炮打到,因此不能放大炮。
類比於USACO玉米田的一道題,咱們能夠在輸入的時候直接處理出初始狀態,即山地確定不能放炮。用\(F[i]\)數組存儲。
可是咱們發現了一個問題。。這麼設置狀態所需的空間是:100 1024 1024。。必爆無疑。
怎麼辦呢?
在此介紹動態規劃中的經常使用優化方法:滾動數組。
代碼:
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; int n,m,cnt; int map[110][20]; int dp[110][70][70]; int F[110]; int num[110],st[110]; //dp[i][j][k]表示行數爲i,狀態爲j、上一行狀態爲k的時候最多擺放的炮兵部隊的個數。 int lowbit(int x) { int ret=0; while(x) { x-=(x&(-x)); ret++; } return ret; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { char a; cin>>a; if(a=='H') map[i][j]=1; F[i]=(F[i]<<1)+map[i][j]; } st[++cnt]=0; for(int i=1;i<(1<<m);i++) { if(i&(i<<1)) continue; if(i&(i<<2)) continue; if(i&(i>>1)) continue; if(i&(i>>2)) continue; st[++cnt]=i; int tmp=i; num[cnt]=lowbit(tmp); } for(int i=1;i<=cnt;i++) if((st[i]&F[1])==0) dp[1][i][0]=num[i]; for(int i=1;i<=cnt;i++) if((st[i]&F[2])==0) for(int j=1;j<=cnt;j++) if((st[i]&st[j])==0 && (st[j]&F[1])==0) dp[2][i][j]=num[i]+num[j]; for(int i=3;i<=n;i++) for(int j=1;j<=cnt;j++) if((st[j]&F[i])==0) for(int k1=1;k1<=cnt;k1++) if((st[j]&st[k1])==0 && (st[k1]&F[i-1])==0) for(int k2=1;k2<=cnt;k2++) if((st[j]&st[k2])==0 && (st[k1]&st[k2])==0 && (st[k2]&F[i-2])==0) dp[i][j][k1]=max(dp[i][j][k1],dp[i-1][k1][k2]+num[j]); int ans=0; 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; }