1. 程式人生 > >(狀壓dp)NOI 2001(POJ 1185) 炮兵陣地

(狀壓dp)NOI 2001(POJ 1185) 炮兵陣地

上下 數據 enter 能夠 sam src max spa 參加

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

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

Input

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

Output

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

Sample Input

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

Sample Output

6

這兩天室友和幾個同系的同學都去參加了北大ACM暑期學校,借著這個光,我也跟著看北大老師的ppt和做相應題目學習了一個。

其實這道題本身對於我還是有些挑戰性的,看了ppt中講解的思路,就豁然開朗了。

用dp[i][j][k]記錄第i行狀態為j,第i-1行狀態為k的最大炮兵部隊擺放個數。

dp[i][j][k] = max{dp[i-1][k][m], m = 0...1023} + Num(j),

在不優化時一行的狀態數有2^10=1024個,可是註意到其中許多是顯然不符合題意的。故需要預處理一下,將不符合的直接去掉,通過簡單的DFS就可以得出總共恰好有60種情況。(註意一行什麽都不填也是一種)這是本題非常重要的突破點,通過這個時間復雜度就降了下來。

接下來還需要對第一行的情況特殊處理一下,然後就依照上述狀態轉移方程進行下去就可以了。

(NOI題目ac數++ ~~)

  1 #include <iostream>
  2 #include <string>
  3
#include <algorithm> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <queue> 8 #include <set> 9 #include <map> 10 #include <list> 11 #include <vector> 12 #include <stack> 13 #define mp make_pair 14 //#define P make_pair 15 #define MIN(a,b) (a>b?b:a) 16 //#define MAX(a,b) (a>b?a:b) 17 typedef long long ll; 18 typedef unsigned long long ull; 19 const int MAX=110; 20 const int INF=1e8+5; 21 using namespace std; 22 //const int MOD=1e9+7; 23 typedef pair<ll,int> pii; 24 const double eps=0.00000001; 25 //map<int,int>id_code,id_num; 26 int id_code[70],id_num[70]; 27 int cnt,ge; 28 void dfs(int code,int lo,int num) 29 { 30 for(int i=lo+3;i<10;i++) 31 { 32 dfs(code|(1<<i),i,num+1); 33 } 34 id_num[cnt]=num; 35 id_code[cnt++]=code; 36 } 37 int dp[MAX][70][70]; 38 int n,m; 39 char tem[15]; 40 int code; 41 int an; 42 bool check(int x,int y)//擺法x和地形y是否沖突 43 { 44 int legal=x&y;//擺上且符合地形的部分 45 int illegal=x^legal;//擺上且不符合地形的部分 46 if(illegal) 47 return false; 48 return true; 49 } 50 int main() 51 { 52 for(int i=0;i<10;i++) 53 dfs(1<<i,i,1); 54 memset(dp,0,sizeof(dp)); 55 id_num[cnt]=0; 56 id_code[cnt++]=0; 57 while(~scanf("%d%d",&n,&m)) 58 { 59 code=0; 60 an=0; 61 scanf("%s",tem); 62 for(int j=0;j<m;j++) 63 if(tem[j]==P) 64 code|=(1<<j); 65 for(int r=0;r<cnt;r++)//特殊處理第一行 66 { 67 if(check(id_code[r],code))//這一行可以這麽擺,與地形不沖突 68 { 69 for(int p=0;p<cnt;p++) 70 { 71 if(id_code[r]&id_code[p]) 72 continue; 73 dp[0][r][p]=id_num[r];//第一行的炮兵最多個數至於這一行怎麽擺有關 74 } 75 } 76 } 77 for(int i=1;i<n;i++) 78 { 79 code=0; 80 scanf("%s",tem); 81 for(int j=0;j<m;j++) 82 if(tem[j]==P) 83 code|=(1<<j); 84 for(int r=0;r<cnt;r++) 85 { 86 if(check(id_code[r],code))//這一行可以這麽擺,與地形不沖突 87 { 88 for(int p=0;p<cnt;p++) 89 for(int q=0;q<cnt;q++)//前兩行狀態 90 { 91 if((id_code[p]&id_code[q])||(id_code[r]&id_code[p])||(id_code[r]&id_code[q]))//沖突則 92 continue; 93 dp[i][r][p]=max(dp[i][r][p],dp[i-1][p][q]+id_num[r]); 94 } 95 } 96 } 97 } 98 for(int i=0;i<cnt;i++) 99 for(int j=0;j<cnt;j++) 100 an=max(an,dp[n-1][i][j]); 101 printf("%d\n",an); 102 } 103 return 0; 104 }

(狀壓dp)NOI 2001(POJ 1185) 炮兵陣地