炮兵阵地(状压dp)

描述

传送门:poj-1185

 司令部的将军们打算在NM的网格地图上部署他们的炮兵部队。一个NM的地图由N行M列组成,地图的每一格可能是山地(用”H” 表示),也可能是平原(用”P”表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

 如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

Input

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

Output

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

Examples

  • intput

    1
    2
    3
    4
    5
    6
    5 4
    PHPP
    PPHH
    PPPP
    PHPP
    PHHP
  • output

    1
    6

思路

  • 对于每行都用一个二进制数来表示这一行的地图,1表示山地,0表示平原。
  • 用一个$N$位二进制数表示每行放炮兵的状态,1表示放,0表示不放。!(i<<1&i)&&!(i<<2&i) 表示没有两个1相邻,也没有两个1之间只有一个0,那么这个状态合法。
  • $j$状态能放到$i$行的条件是: j&Map[i]==0,即没有山地和炮兵重合。同样的,$i$行,$i-1$行,$i-2$行都不能有炮兵重合,重合了就能互相攻击到。
  • $dp[i][j][k]$: $i$行为状态$j$,$i-1$行为状态$k$时最大ans,状态转移方程就是:

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/*
User: Armin
Memory: 3332K Time: 250MS
Language: G++ Result: Accepted
*/
#include<stdio.h>
#include<math.h>
#include<vector>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define repd(i,a,n) for(int i=n-1;i>=a;i--)
#define CRL(a,x) memset(a,x,sizeof(a))
const int N=1e2+5;

int dp[N][N][N],n,m,Map[N],Soldier[N]; //dp[i][j][k]:i行为状态j,i-1行为状态k时最大ans
vector<int> ok;
char x;

int Count(int x){int sum=0;while(x){sum++;x-=x&-x;}return sum;}

int main()
{
scanf("%d%d",&n,&m);

rep(i,1,n+1){ //预处理每行状态,每行用一个二进制数表示
getchar();
rep(j,0,m)
Map[i]= Map[i]<<1|getchar()=='H';
}

rep(i,0,1<<m) //预处理出所有合法的状态,放在vector中
if(!(i<<1&i)&&!(i<<2&i)){
Soldier[ok.size()]=Count(i);
ok.push_back(i);
}

rep(i,0,ok.size()) //预处理第一排,枚举所有合法状态
if(!(Map[1]&ok[i])) //可以放到这排
dp[1][i][0]=Soldier[i];

rep(i,2,n+1)
rep(j,0,ok.size()){
if(Map[i]&ok[j]) continue; //和i行有冲突,跳过
rep(k,0,ok.size()){
if(Map[i-1]&ok[k]||ok[j]&ok[k]) continue; //合法状态和i-1行有冲突或者i行状态和i-1行冲突,跳过
rep(l,0,ok.size()){
if(Map[i-2]&ok[l]||ok[l]&ok[k]||ok[l]&ok[j]) continue;//合法状态和i-2行有冲突或 i-2行和i行有冲突 或 i-2行和i-1行有冲突,跳过
dp[i][j][k]=max(dp[i-1][k][l]+Soldier[j] ,dp[i][j][k]); //状态转移
}
}
}

int ans=0;
for(int i=0; i<ok.size(); i++)
for(int j=0; j<ok.size(); j++) //枚举dp[row-1][i][j]
ans=max(ans,dp[n][i][j]);
printf("%d\n",ans);

return 0;
}
-------------本文结束 感谢您的阅读-------------

本文标题:炮兵阵地(状压dp)

文章作者:Armin

发布时间:2018年11月08日 - 11:11

最后更新:2018年11月08日 - 12:11

原始链接:http://x-armin.com/炮兵阵地/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

小礼物走一波