司令部的将军们打算在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 }