poj-1185炮兵阵地
炮兵阵地
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 15742 | Accepted: 5957 |
Description
司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
Input
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
Output
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
Sample Input
5 4 PHPP PPHH PPPP PHPP PHHP
Sample Output
6
地址:http://poj.org/problem?id=1185
一下午加一晚上,状态压缩dp,实在是。。。。。。。。照着一个很详细的代码和注释,一边敲一边标注释,现在养成了标注释的好习惯,以前以为没有用。。。。解释不出来了。全在注释里,很详细,我的第一道状压dp,例题,也不是我自己做的,我都多久没自己做一道题了,只有在比赛的时候才能真正自己做上一两道。。。。。。。
代码:
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <vector> #include <queue> #include <algorithm> using namespace std; const int maxx=100000;//列数最大不超过10 int stack[maxx]; int len=0; char map[105][15]; int n,m,power=1; int dp[100][100][150]; int find(int now,int last,int n){//now:当前行last:上一行 int ret=0,sz=0; int tmp=stack[now]|stack[last];//放了炮的列数 if(dp[now][last][n]!=-1)return dp[now][last][n]; for(int i=0;i<m;++i){ if((stack[now]&(~(1<<i)))!=stack[now])++sz;//sz为now这行有几个炮 } if(n==1){//到第一行返回第一行有几个炮 dp[now][last][1]=sz; return sz; } for(int i=0;i<len;++i)//判断now-2行 { int flag=0; if(tmp&stack[i])continue;//tmp是底下两行的炮在哪列,与now-2行不能冲突,冲突则continue for(int j=0;j<m;++j) { if(map[n-2][j]=='H'&&(stack[i]&(~(1<<j)))!=stack[i]){ flag=1;break; } } if(!flag)ret=max(ret,find(last,i,n-1)); } ret+=sz; dp[now][last][n]=ret; return ret; } int main() { int ans=0; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { map[0][i]='H'; } for(int i=0;i<m;++i){//一行有m个位置,每个位置可以有两种状态,一行共2^m种状态 eg:10010 power*=2; } for(int i=1;i<=n;i++) { scanf("%s",map[i]); } for(int i=0;i<power;++i)//每个i都代表此行的一种状态,从00001到11111共power种 { int flag=0; for(int j=0;j<m;++j)//表示第i种状况的第j位,如01001的第2位是1 { if((i&(~(1<<j)))!=i){//如果i状况下第j位是1,即放了炮,那么j-2,j-1,j+1,j+2四个位置均不能放 if(j-2<=m&&j-2>0&&(i&(~(1<<(j-2))))!=i){//j-2位置在1~m范围内且j-2是1 flag=1; break; } if(j-1<=m&&j-1>0&&(i&(~(1<<(j-1))))!=i){//j-1位置在1~m范围内且j-1是1 flag=1; break; } if(j+1<=m&&j+1>0&&(i&(~(1<<(j+1))))!=i){//j+1位置在1~m范围内且j+1是1 flag=1; break; } if(j+2<=m&&j+2>0&&(i&(~(1<<(j+2))))!=i){//j+2位置在1~m范围内且j+2是1 flag=1; break; } } } if(!flag)stack[len++]=i;//stack数组里存放的i是一行里所有可能的情况,减去了那些在攻击范围内的不可能情况 } memset(dp,-1,sizeof(dp)); for(int i=0;i<len;++i)//当前行的情况,有len种 { for(int j=0;j<len;++j){//当前行的上一行,每行的上一行都要考虑len种情况 int flag=0; if((stack[i]&stack[j]))continue;//当前行是stack[i],它的上一行是stack[j],如果两个1同列,那么不合格继续continue for(int k=0;k<m;++k){//判断此行中哪个是hill,H不能放炮 if(map[n][k]=='H'&&(stack[i]&(~(1<<k)))!=stack[i]){//k位置有炮而且k位置是hill flag=1;break; } if(map[n-1][k]=='H'&&(stack[j]&(~(1<<k)))!=stack[j]){//上一行k位置有炮而且k位置是hill flag=1;break; } } if(!flag)ans=max(ans,find(i,j,n)); } } printf("%d\n",ans); return 0; }