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 }

 

 

原文地址:https://www.cnblogs.com/ljh2000-jump/p/5930741.html