1. 程式人生 > >「Luogu」2704炮兵陣地

「Luogu」2704炮兵陣地

情況 mem dig end continue upload mes 攻擊 amp

題意:原題在這

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

技術分享圖片

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

思路:

1. 用二進制,1表示這個位置上放了炮兵。由於炮兵攻擊範圍是2,所以枚舉前兩行的狀態,一橫行一橫行向下掃。

2. dp[i][j][k]表示當前選到了第i行,當前的狀態為j時,前一行的的狀態為k時的最大數量,有4重循環,由上一行枚舉到這一行。

情況判斷:

1. 判斷每個位置是不是山丘:

把每一行的輸入都轉成一個二進制數(平原是 0,山丘是1),然後&一下當前狀態,如果位運算結果不是零,說明有些炮兵放在了山丘上,不能選。

2. 判斷每個狀態炮兵能不能互相攻擊:

如果把當前狀態的二進制數位運算左移一位或兩位,用這個結果&原狀態,如果結果不是 0,那麽就一定存在兩個炮兵能互相攻擊。即 x&(x<<1),x&(x<<2);

3. 判斷每一列之前兩行有沒有炮兵
這個就直接用當前狀態分別"&"之前的兩行即可,如果與操作結果不為零,說明有若前兩行有炮兵。

代碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define maxn 
#define inf 9999999999
using namespace std;

vector
<int>v; int n,m,digit; int check[105]; int num[105];//記錄炮兵個數 int dp[105][105][105]; bool map[105][105]; bool judge(int x)//判斷能不能放 { if(x&(x<<1)) return 0; if(x&(x<<2)) return 0; else return 1; } int calv(int x)//計算每種情況的炮兵個數 { int ans=0; while(x) { if((x&1)) ans++; x=x>>1; } return ans; } int main() { memset(dp,-1,sizeof(dp)); cin>>n>>m; digit=(1<<m)-1; int tmp=0; for(int i=0;i<=digit;i++)//預處理出所有可能的狀態並用vector數組記錄 { if(judge(i)) { v.push_back(i); num[tmp]=calv(i); tmp++; } } char pos; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { cin>>pos; if(pos==P) map[i][j]=1; else map[i][j]=0; check[i-1]+=(map[i][j]<<(j-1)); //將地圖轉為二進制,如果為1,則可以放炮兵 } int cnt=v.size()-1; for(int i=0;i<=cnt;i++) { if((v[i]|check[0])==check[0]) { dp[0][i][0]=num[i];//初始化,第0列不受前面列的影響,因此初始值就是這種狀態的炮兵數量 } } for(int i=1;i<n;i++)//枚舉行 for(int j=0;j<=cnt;j++)// 枚舉當前狀態 { if((v[j]|check[i])!=check[i]) continue; //判斷當前的狀態能否在地圖上放置 for(int x=0;x<=cnt;x++)//枚舉上一行的狀態 { if(v[x]&v[j]) continue;//判斷前一行的炮兵能否打到當前行的炮兵 if((v[x]|check[i-1])!=check[i-1]) continue;//判斷前一行的炮兵能否放在地圖上 for(int y=0;y<=cnt;y++)// 枚舉上一行的上一行的狀態 { if(dp[i-1][x][y]==-1) continue; //判斷上一種情況是否存在 if(v[y]&v[j]) continue; //判斷兩行前的炮兵會不會達到當前行的炮兵 if(v[y]&v[x]) continue; //判斷兩行前的炮兵會不會達到上一行的炮兵 if((v[y]|check[i-2])!=check[i-2]) continue; //判斷兩行前的炮兵能否放置在地圖上 dp[i][j][x]=max(dp[i-1][x][y]+num[j],dp[i][j][x]); } } } int ans=0; for(int i=0;i<=cnt;i++) for(int j=0;j<=cnt;j++) { ans=max(dp[n-1][i][j],ans); } cout<<ans<<endl; return 0; }

「Luogu」2704炮兵陣地