首页 > 代码库 > POJ1185 炮兵阵地

POJ1185 炮兵阵地

题目描述 Description

司令部的将军们打算在N × M的网格地图上部署他们的炮兵部队。一个N × M的地图由N行M列组成,地图的每一格可能是山地(用"H"表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队 (山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。 现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

输入描述 Input Description

第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符(‘P‘或者‘H‘),中间没有空格。按顺序表示地图中每一行的数据。N ≤ 100, M ≤ 10。

输出描述 Output Description

仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。

样例输入 Sample Input

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

样例输出 Sample Output

6

 

 

正解:状压DP

解题报告:

  今天考试T4。

  这是一道状压DP裸题,我一上来就看到这道题,就知道是NOI2001原题,然而我并没有想到怎么做。因为我一直很纠结这一行的决策会受上两行的影响,所以总在想着用一个什么三进制数来表示,然而总是没想通。其实并不需要三进制,就用常规的二进制思路就可以做这道题了。我用一个二进制数S1表示放炮兵,0表示不放,显然对于每一行可以预处理一下哪些状态是可行的。位运算可以大大加速预处理速度。并且我们可以发现,一行的可行的状态数很少,而且绝对不会超过60。那么这就很好做了,我预处理出每一行的可行状态,存下来,并且算一下当前状态下的炮兵数量,每次枚举的时候我只考虑这些可行状态,就可以大大加速,避免了许多无用状态的讨论。然后,因为当前行的决策要受上两行的影响,既然要受影响,那么我不妨把状态记下来。我用f[i][S1][S2]表示第i行放状态为S1i-1行放S2的最大值。这样的话转移也很明了了,我枚举一个i-2行的状态K1,所以f[i][S1][S2]=max(f[i-1][S2][K1]+cnt[S1],f[i][S1][S2]),也就是说我每次需要枚举三个状态。显然,第一行我们需要特殊处理,直接把第一行的所有情况算出来,为了方便处理边界,我可以给第0行增加一种状态0,这样方便转移。其余的就是一些细节了,比如果S1S2K1互不冲突的判断,位运算可以随便过啦。

 

 

 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 炮兵阵地