首页 > 代码库 > POJ1185 炮兵阵地
POJ1185 炮兵阵地
司令部的将军们打算在N × M的网格地图上部署他们的炮兵部队。一个N × M的地图由N行M列组成,地图的每一格可能是山地(用"H"表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队 (山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。 现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符(‘P‘或者‘H‘),中间没有空格。按顺序表示地图中每一行的数据。N ≤ 100, M ≤ 10。
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
6
正解:状压DP
解题报告:
今天考试T4。
这是一道状压DP裸题,我一上来就看到这道题,就知道是NOI2001原题,然而我并没有想到怎么做。因为我一直很纠结这一行的决策会受上两行的影响,所以总在想着用一个什么三进制数来表示,然而总是没想通。其实并不需要三进制,就用常规的二进制思路就可以做这道题了。我用一个二进制数S,1表示放炮兵,0表示不放,显然对于每一行可以预处理一下哪些状态是可行的。位运算可以大大加速预处理速度。并且我们可以发现,一行的可行的状态数很少,而且绝对不会超过60。那么这就很好做了,我预处理出每一行的可行状态,存下来,并且算一下当前状态下的炮兵数量,每次枚举的时候我只考虑这些可行状态,就可以大大加速,避免了许多无用状态的讨论。然后,因为当前行的决策要受上两行的影响,既然要受影响,那么我不妨把状态记下来。我用f[i][S1][S2]表示第i行放状态为S1,i-1行放S2的最大值。这样的话转移也很明了了,我枚举一个i-2行的状态K1,所以f[i][S1][S2]=max(f[i-1][S2][K1]+cnt[S1],f[i][S1][S2]),也就是说我每次需要枚举三个状态。显然,第一行我们需要特殊处理,直接把第一行的所有情况算出来,为了方便处理边界,我可以给第0行增加一种状态0,这样方便转移。其余的就是一些细节了,比如果S1、S2、K1互不冲突的判断,位运算可以随便过啦。
1 //It is made by jump~ 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector>10 #include <queue>11 #include <map>12 #include <set>13 using namespace std;14 typedef long long LL;15 const int inf = (1<<30);16 const int MAXN = 1011;17 const int MAXM = 5000011;18 int n,m,ans,end;19 int a[MAXN][12];20 char ch[12];21 int f[MAXN][100][100];//f[i][S1][S2]表示第i行放状态为S1,i-1行放S2的最大值22 int dix[MAXN];23 int mp[MAXN][100],cnt[MAXN][100],num[MAXN];//每一行可行状态最多60种24 25 inline int getint()26 {27 int w=0,q=0; char c=getchar();28 while((c<‘0‘ || c>‘9‘) && c!=‘-‘) c=getchar(); if(c==‘-‘) q=1,c=getchar(); 29 while (c>=‘0‘ && c<=‘9‘) w=w*10+c-‘0‘, c=getchar(); return q ? -w : w;30 }31 32 inline int get_cnt(int s){ int total=0; while(s) total+=s&1,s>>=1; return total; }33 34 inline void work(){35 n=getint(); m=getint();36 for(int i=1;i<=n;i++) {37 scanf("%s",ch);38 for(int j=0;j<m;j++) {39 if(ch[j]==‘P‘) a[i][j+1]=1;40 else a[i][j+1]=0;41 dix[i]|=a[i][j+1]*(1<<j);42 }43 }44 end=(1<<m)-1;45 for(int i=1;i<=n;i++) {46 for(int s=0;s<=end;s++) {47 if(( s&(s<<1) )!=0) continue; if(( s&(s<<2) )!=0) continue;48 if((dix[i]&s)!=s) continue;49 num[i]++; mp[i][num[i]]=s; cnt[i][num[i]]=get_cnt(s);50 }51 }52 num[0]++; for(int i=1;i<=n;i++) for(int j=1;j<=num[1];j++) f[1][j][1]=cnt[1][j];53 int now,k1,k2;54 for(int i=2;i<=n;i++) {55 for(int j=1;j<=num[i-1];j++) {56 k1=mp[i-1][j];57 for(int k=1;k<=num[i-2];k++) {58 k2=mp[i-2][k]; if(f[i-1][j][k]==0) continue;59 for(int l=1;l<=num[i];l++) {60 now=mp[i][l]; if((now&k1)!=0) continue; if((now&k2)!=0) continue;61 f[i][l][j]=max(f[i][l][j],f[i-1][j][k]+cnt[i][l]);62 }63 }64 }65 }66 for(int i=1;i<=num[n];i++) for(int j=1;j<=num[n-1];j++) ans=max(ans,f[n][i][j]);67 printf("%d",ans);68 }69 70 int main()71 {72 work();73 return 0;74 }
POJ1185 炮兵阵地