(状压dp)NOI 2001(POJ 1185) 炮兵阵地

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

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

Input

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

Output

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

Sample Input

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

Sample Output

6

这两天室友和几个同系的同学都去参加了北大ACM暑期学校,借着这个光,我也跟着看北大老师的ppt和做相应题目学习了一个。

其实这道题本身对于我还是有些挑战性的,看了ppt中讲解的思路,就豁然开朗了。

用dp[i][j][k]记录第i行状态为j,第i-1行状态为k的最大炮兵部队摆放个数。

dp[i][j][k] = max{dp[i-1][k][m], m = 0...1023} + Num(j),

在不优化时一行的状态数有2^10=1024个,可是注意到其中许多是显然不符合题意的。故需要预处理一下,将不符合的直接去掉,通过简单的DFS就可以得出总共恰好有60种情况。(注意一行什么都不填也是一种)这是本题非常重要的突破点,通过这个时间复杂度就降了下来。

接下来还需要对第一行的情况特殊处理一下,然后就依照上述状态转移方程进行下去就可以了。

(NOI题目ac数++ ~~)

  1 #include <iostream>
  2 #include <string>
  3 #include <algorithm>
  4 #include <cstring>
  5 #include <cstdio>
  6 #include <cmath>
  7 #include <queue>
  8 #include <set>
  9 #include <map>
 10 #include <list>
 11 #include <vector>
 12 #include <stack>
 13 #define mp make_pair
 14 //#define P make_pair
 15 #define MIN(a,b) (a>b?b:a)
 16 //#define MAX(a,b) (a>b?a:b)
 17 typedef long long ll;
 18 typedef unsigned long long ull;
 19 const int MAX=110;
 20 const int INF=1e8+5;
 21 using namespace std;
 22 //const int MOD=1e9+7;
 23 typedef pair<ll,int> pii;
 24 const double eps=0.00000001;
 25 //map<int,int>id_code,id_num;
 26 int id_code[70],id_num[70];
 27 int cnt,ge;
 28 void dfs(int code,int lo,int num)
 29 {
 30     for(int i=lo+3;i<10;i++)
 31     {
 32         dfs(code|(1<<i),i,num+1);
 33     }
 34     id_num[cnt]=num;
 35     id_code[cnt++]=code;
 36 }
 37 int dp[MAX][70][70];
 38 int n,m;
 39 char tem[15];
 40 int code;
 41 int an;
 42 bool check(int x,int y)//摆法x和地形y是否冲突
 43 {
 44     int legal=x&y;//摆上且符合地形的部分
 45     int illegal=x^legal;//摆上且不符合地形的部分
 46     if(illegal)
 47         return false;
 48     return true;
 49 }
 50 int main()
 51 {
 52     for(int i=0;i<10;i++)
 53         dfs(1<<i,i,1);
 54     memset(dp,0,sizeof(dp));
 55     id_num[cnt]=0;
 56     id_code[cnt++]=0;
 57     while(~scanf("%d%d",&n,&m))
 58     {
 59         code=0;
 60         an=0;
 61         scanf("%s",tem);
 62         for(int j=0;j<m;j++)
 63             if(tem[j]==‘P‘)
 64                 code|=(1<<j);
 65         for(int r=0;r<cnt;r++)//特殊处理第一行
 66         {
 67             if(check(id_code[r],code))//这一行可以这么摆,与地形不冲突
 68             {
 69                 for(int p=0;p<cnt;p++)
 70                 {
 71                     if(id_code[r]&id_code[p])
 72                         continue;
 73                     dp[0][r][p]=id_num[r];//第一行的炮兵最多个数至于这一行怎么摆有关
 74                 }
 75             }
 76         }
 77         for(int i=1;i<n;i++)
 78         {
 79             code=0;
 80             scanf("%s",tem);
 81             for(int j=0;j<m;j++)
 82                 if(tem[j]==‘P‘)
 83                     code|=(1<<j);
 84             for(int r=0;r<cnt;r++)
 85             {
 86                 if(check(id_code[r],code))//这一行可以这么摆,与地形不冲突
 87                 {
 88                     for(int p=0;p<cnt;p++)
 89                         for(int q=0;q<cnt;q++)//前两行状态
 90                         {
 91                             if((id_code[p]&id_code[q])||(id_code[r]&id_code[p])||(id_code[r]&id_code[q]))//冲突则
 92                                 continue;
 93                             dp[i][r][p]=max(dp[i][r][p],dp[i-1][p][q]+id_num[r]);
 94                         }
 95                 }
 96             }
 97         }
 98         for(int i=0;i<cnt;i++)
 99             for(int j=0;j<cnt;j++)
100                 an=max(an,dp[n-1][i][j]);
101         printf("%d\n",an);
102     }
103     return 0;
104 }
时间: 2024-12-15 12:58:08

(状压dp)NOI 2001(POJ 1185) 炮兵阵地的相关文章

POJ 1185 炮兵阵地 状压dp

http://poj.org/problem?id=1185 经典题目不必多说,直接贴代码. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int n, m, cnt, size; 7 int a[110], st[70], ct[70]; 8 char str[15]; 9 int f[110][70][70]; 10 void init(

POJ 1185 炮兵阵地 状压DP+离散化优化

一开始能想到的状态就只有位压两行和当前行的行号,这样无论是空间和时间都是无法接受的. 但是因为炮兵的攻击范围比较大,而且又有地形限制,每一行的状态其实不多了,打表看了一下不超过80种,离散化一下就可以随意DP了. 据说题目也可以抽象成二分图最大匹配来搞?感觉复杂度有点高 #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <set> #i

POJ 1185 炮兵阵地(动态规划)

炮兵阵地 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 19152   Accepted: 7417 Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击

POJ 1185 炮兵阵地 (状压dp 经典中的经典)

炮兵阵地 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 21381   Accepted: 8290 Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击

POJ 1185 炮兵阵地(状压DP入门)

http://poj.org/problem?id=1185 状压DP: 1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int dp[105][100][100]; 7 int ma[105],st[105]; 8 9 int ok(int x) 10 { 11 return (x&(x<<1))+(x&(x&

状压DP [POJ 1185] 炮兵阵地

炮兵阵地 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 19922   Accepted: 7709 Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击

POJ 1185 炮兵阵地 (状压DP)

题目链接 题意 : 中文题不详述. 思路 :状压DP,1表示该位置放炮弹,0表示不放.dp[i][j][k],代表第 i 行的状态为k时第i-1行的状态为 j 时放置的最大炮弹数.只是注意判断的时候不要互相攻击到就可以了,还要与地形相适应. 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 6 using namespace std

POJ 1185炮兵阵地

解题思路: 简单的状压DP,1表示放炮,预处理出每一行所有两个1间隔不小于2的状态,每一行的状态只和上面两行有关,因此可以枚举这三行的状态,用DP[i][j][k]表示第i行状态为k,第i-1行状态为j的数目,转移方程为dp[i][j][k] = max(dp[i][j][k], dp[i-1][l][j] +count(k)); #include <iostream> #include <cstring> #include <cstdlib> #include <

poj 1185 炮兵阵地(状压DP)

炮兵阵地 \(solution:\) 这一道题限制条件有点多,我们需要逐个击破: 首先要判断这一格是否为山地,这个可以状态压缩 然后我们的左右一定距离不能有炮兵,这是一个突破口,因为我们看数据发现每行不超过十个格子!这样的话我们完全可以预处理出来每一行的填充方案而且这些方案肯定很少! 某一格的前面两格也不能有炮兵.这个限制最麻烦,他涉及了前两行,我们如果要从第一行开始向下推,那就必须知道前两行的所有情况!但是这些在极小的数据范围面前都可以想办法解决. 这是这道题的三个关键点,然后我们可以想到用前

POJ 1185炮兵阵地 (状压DP)

题目链接 POJ 1185 今天艾教留了一大堆线段树,表示做不动了,就补补前面的题.QAQ 这个题,我第一次写还是像前面HDU 2167那样写,发现这次影响第 i 行的还用i-2行那样,那以前的方法就行不通了. 找出所有可行的状态,因为每一行最大只有10列,所以一行里最多有4个,那它可行的状态不多(网上大多数说法最多是60个).用dp[x][i][j]来转移,x表示第x行,i表示第x行的状态,j表示第x-1行的状态.先初始化前两行. 1 #include <cstdio> 2 #include