POJ1185 炮兵阵地 状态压缩DP

B - 炮兵阵地

Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u

Submit Status

Appoint description: 
System Crawler  (2014-06-29)

Description

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

转自 :http://chuanwang66.iteye.com/blog/1467227

1. 为何状态压缩:

棋盘规模为n*m,且m≤10,如果用一个int表示一行上棋子的状态,足以表示m≤10所要求的范围。故想到用int s[num]。至于开多大的数组,可以自己用DFS搜索试试看;也可以遍历0~2^m-1,对每个数值的二进制表示进行检查;也可以用数学方法(?)

2. 如何构造状态:

当然,在此之前首先要想到用DP(?)。之后,才考虑去构造状态函数f(...)。

这里有一个链式的限制 :某行上的某个棋子的攻击范围是2。即,第r行的状态s[i],决定第r-1行只能取部分状态s[p];同时,第r行的状态s[i],第r-1行状态s[p],共同决定第r-2行只能取更少的状态s[q]。当然,最后对上面得到的候选s[i], s[p], s[q],还要用地形的限制去筛选一下即可。

简言之,第r行的威震第r-2行,因此在递推公式(左边=右边)中,必然同时出现r,和r-2两个行标;由于递推公式中行标是连续出现的,故在递推公式中必然同时出现r, r-1和r-2三个行标。由于在递推公式中左边包含一个f(...),右边包含另一个f(...),根据抽屉原理,r, r-1, r-2中至少有两个在同一个f(...)中,因此状态函数中必然至少包括相邻两行的行号作为两个维度。这就是为什么状态函数要涉及到两(相邻的)行,而不是一行。能想到的最简单形式如下:

dp[r][i][p]:第r行状态为s[i],第r-1行状态为s[p],此时从第0行~第r行棋子的最大数目为dp[r][i][p]

递推公式:

s[p]影响到s[q]的选取

----

|    |

dp[r][i][p]=max{dp[r-1][p][q]}+sum[j], 其中sum[j]是状态s[j]中1的个数

|   |                             |

----                             |

s[i]影响到s[p]的选取                 |

|                                 |

----------------------------

转自:http://www.cnblogs.com/ZShogg/archive/2013/05/11/3072534.html

题意:给出一张n*m的地图,‘H‘表示高地,不能部署炮兵,‘P‘表示平原,可以部署炮兵,炮兵之间必须保持横向、纵向至少2个格子的距离,保证没有误伤。问最多可以部署多少炮兵。

分析:

1.可以用一个32位整数存每一行的状态(二进制上1表示有布置炮兵,0表示没有布置炮兵),由于每一行的状态都要前两行的状态来决定,因此真正的一个状态应该包含本行的状态和上一行的状态,用dp[x][i][j]表示第x行上状态i,x-1行状态为j的最优解。

2.每一行的状态其实只有60种,可以直接暴力搜索出来,把这60种状态按二进制递增顺序(排序目的是方便确定状态没有超出n*m的范围)存到数组state[]中,计算好每种状态中能部署炮兵的数目,存到get[]数组。

3.地图转换成一个二进制的数组,即一个整数表示一行的地形(1表示高地,0表示平原)。因此一个状态能否存在的一个条件是state[i] & map[x] == 0,这样是合法的.  (注意取反存,方便判断)

4.状态转移方程为dp[x][i][j] = max(dp[x - 1][j][k] + get[i],dp[x][i][j]),条件是state[i] & map[x] == 0 ,state[i] & state[j] == 0,state[i] & state[k] == 0,而且存在dp[x - 1][j][k]。把不存在的dp[x][i][j]标为-1.

PS:看了discuss发现还可以用最大点独立集来做。

上面的链接 还有相关滚动数组的做法,orz。

经典就是经典啊~~~~~

注意点:

1.状态涉及本行与上行。

2.需要先筛选一遍可行状态,不然会T。

3.要将地形压缩,不然会T。压缩时取反,高地表示1.

4.还可以用滚动数组做~~

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<cstdio>
  5 #include<algorithm>
  6 #include<cmath>
  7 #include<queue>
  8 #include<map>
  9 #include<vector>
 10
 11 #define N 10005
 12 #define M 100000
 13 #define mod 1000000007
 14 #define mod2 100000000
 15 #define ll long long
 16 #define maxi(a,b) (a)>(b)? (a) : (b)
 17 #define mini(a,b) (a)<(b)? (a) : (b)
 18
 19 using namespace std;
 20
 21 int n,m;
 22 char s[110][20];
 23 int dp[110][110][110];
 24 int a[110];
 25 int ans;
 26 int tot;
 27 int cc[ (1<<10)+10 ];
 28 int ok[ (1<<10)+10 ];
 29
 30 void ini1()
 31 {
 32     int i,j;
 33     for(i=0;i<(1<<10);i++){
 34         for(j=0;j<10;j++){
 35             if( ( (i&(1<<j))!=0) ){
 36                 cc[i]++;
 37             }
 38         }
 39     }
 40 }
 41
 42 bool isok(int x)
 43 {
 44     if(x&(x<<1))return false;
 45     if(x&(x<<2))return false;
 46     return true;
 47 }
 48
 49
 50 void ini2()
 51 {
 52     memset(ok,0,sizeof(ok));
 53     for(int i=0;i<(1<<m);i++) //i枚举所有m位的二进制数
 54     {
 55         if(isok(i))
 56         {
 57             ok[tot]=i;
 58             tot++;
 59         }
 60     }
 61 }
 62
 63 void ini()
 64 {
 65     int i,j;
 66     ans=0;
 67    // tot=1<<m;
 68     tot=0;
 69     ini2();
 70     memset(dp,0,sizeof(dp));
 71     memset(a,0,sizeof(a));
 72     for(i=1;i<=n;i++){
 73         scanf("%s",s[i]);
 74     }
 75     //a[0]=a[1]=tot-1;
 76     for(i=1;i<=n;i++){
 77         for(j=0;j<m;j++){
 78             if( s[i][ j ]==‘H‘ ){
 79                 a[i]+=(1<<j);
 80             }
 81         }
 82     }
 83
 84    // for(i=1;i<=n;i++){
 85     //    printf("  i=%d a=%d\n",i,a[i]);
 86     //}
 87 }
 88
 89
 90 void solve()
 91 {
 92     int o,i,j,k;
 93     for(i=1;i<=n;i++){
 94         for(o=0;o<tot;o++){
 95             if( (ok[o] & a[i] )!=0 ) continue;
 96             if(i==1){
 97                 dp[i][o][0]=cc[ ok[o] ];continue;
 98             }
 99             for(j=0;j<tot;j++){
100                 if( (ok[j] & a[i-1] )!=0 ) continue;
101                 if( (ok[o] & ok[j] )!=0 ) continue;
102                 for(k=0;k<tot;k++){
103                     if( (ok[k] & a[i-2] )!=0 ) continue;
104                     if( (ok[o] & ok[k] )!=0 ) continue;
105                     if( (ok[j] & ok[k] )!=0 ) continue;
106                     //printf("   i=%d o=%d j=%d k=%d cco=%d ccj=%d dp=%d\n",i,ok[o],ok[j],ok[k],cc[ ok[o] ],cc[ ok[j] ],dp[i-2][ok[k]]);
107                     dp[i][o][ j ]=max(dp[i][ o ][ j ],cc[ ok[o] ]+dp[i-1][ j ][ k ]);
108                 }
109             }
110         }
111     }
112
113     i=n;
114     for(o=0;o<tot;o++){
115         for(j=0;j<tot;j++)
116         ans=max(ans,dp[i][ o ][ j ]);
117     }
118
119     //for(i=1;i<=n;i++){
120    //     for(o=0;o<tot;o++) printf(" i=%d o=%d dp=%d\n",i,ok[o],dp[i][ok[o]]);
121    // }
122 }
123
124 int main()
125 {
126     ini1();
127     //freopen("data.in","r",stdin);
128     //freopen("data.out","w",stdout);
129     //scanf("%d",&T);
130    // for(int cnt=1;cnt<=T;cnt++)
131    // while(T--)
132     while(scanf("%d%d",&n,&m)!=EOF)
133     {
134         ini();
135         solve();
136         printf("%d\n",ans);
137        // printf("%d\n",ma);
138         //cout<<tot<<endl;
139     }
140
141     return 0;
142 }
时间: 2024-10-12 12:28:04

POJ1185 炮兵阵地 状态压缩DP的相关文章

poj 1185 炮兵阵地(状态压缩dp)

Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格.图上其它白色网格均攻击不

炮兵阵地[状态压缩DP]

看到这一道题其实和玉米田很类似,只不过多记录了前两行,其他大体细节差不多,注意滚动数组似乎不滚动更快??? Code #include<bits/stdc++.h> using namespace std; const int N=105; struct state{ int num[105],a[105]; }st[105]; int n,m; char s[15]; int f[3][105][105]; void getstate(int k,char *s){ int t=0; for

HDU1185:炮兵阵地(状态压缩)

Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格.图上其它白色网格均攻击不

poj1185炮兵阵地状压dp

压前两行的状态很容易想到,但是 直接搞  (1<<10) * (1<<10)  空间时间都明显受不了, 但是经过高人指点,你会发现:枚举每一行可行的状态,其实并不多,预先打表处理,不用 1->(1<<10)枚举每一种状态.. 然后记忆化搜就ok了. #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include &

POJ - 1185 炮兵阵地 (状态压缩)

题目大意:中文题目就不多说大意了 解题思路: 1.每行最多仅仅有十个位置,且不是山地就是平原,那么就能够用1表示山地,0表示平原,将每一行的状态进行压缩了 2.接着找出每行能放炮兵的状态.先不考虑其它行放炮兵和该行的山地对其造成的影响,枚举出全部的状态.并记录每一个状态下放的炮兵数量 在上述情况下放炮兵.仅仅须要考虑同行的炮兵是否会相互攻击就能够了,那仅仅须要推断他的左边第一个位置是否有炮兵和左边第二个位置是否有炮兵就可以 3.接着进行dp,由于影响因素有两个.一个是上一行的状态,还有一个是上两

poj1185炮兵布阵结题报告--初步了解--状态压缩dp

好吧,借助poj1185炮兵布阵这题,仔仔细细的了解了一下状态压缩动态规划 首先,借助题目,我们来看看状态压缩是个虾米东西..Ok follow me 一,所谓状态压缩 根据题意,我们得在长度为M 的地图上放置一些大炮(后面简称"放炮",应该不会被和谐吧),那么,首先不考虑山地,我们得把所有的放置方法都找出来,并且注意,这里只对于一行且长度为M(好吧,你可能要问考虑一行,左右互相隔2,互相不在攻击范围,那么上下呢?这里先不急,一步步来) 1,找出所有放炮的方法 假设长度为7,那么看下图

poj1185炮兵阵地

#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <string.h> #include <string> using namespace std; const int MAXN = 100 + 1; //阵地行数 const int MAXM = 10 + 1; //阵地列数 const int State_Num

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

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

POJ 3254 Corn Fields 状态压缩DP (C++/Java)

http://poj.org/problem?id=3254 题目大意: 一个农民有n行m列的地方,每个格子用1代表可以种草地,而0不可以.放牛只能在有草地的,但是相邻的草地不能同时放牛, 问总共有多少种方法. 思路: 状态压缩的DP. 可以用二进制数字来表示放牧情况并判断该状态是否满足条件. 这题的限制条件有两个: 1.草地限制. 2.相邻限制. 对于草地限制,因为输入的时候1是可以种草地的. 以"11110"草地分析,就只有最后一个是不可以种草的.取反后得00001  .(为啥取反