POJ 1185 炮兵阵地(动态规划+状态压缩)
2013-08-23 19:27
405 查看
[b]炮兵阵地[/b]
Description
司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
[b]Input[/b]
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
[b]Output[/b]
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
[b]Sample Input[/b]
[b]Sample Output[/b]
由于M很小,最多为10,所以可以对行进行状态压缩。二进制对应位为1表示放炮兵,为0表示空。我们可以事先生成所有有效的状态,即二进制数任何两个1都要相差两位以上,同时用数组记下此状态有多少个炮兵。对于地形也进行状态压缩,用1表求高地,0表示平原。判断某个状态能否放到某个地形,就是地形状态为1的地方,放置炮兵状态一定为0,这点可以用位运算解决。判断两个状态能否放在相邻行与此相同。
代码如下:
Description
司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
[b]Input[/b]
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
[b]Output[/b]
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
[b]Sample Input[/b]
5 4 PHPP PPHH PPPP PHPP PHHP
[b]Sample Output[/b]
6 分析:思路很简单,因为每个位置要么放一门炮、要么不放,每行最多只有10个位置,所以可以用位存储状态,大体分如下两步: 1.计算出每行所有的合法状态,并用位表示。 2.DP~~~ f[p][i][j] = Max{f[p-1][j][k]+ct(p,i)} f[p][i][j]表示第p行取第i种方案,p-1行取第j种方案时的最大值,ct(p,i)表示第p行用第i种方案时用的炮的个数。 求整数x用二进制表示时1的个数,只需要让x不断的执行x &= (x-1)就可以了,执行了多少次就有多少个1。 为什么呢??可以分两种情况,如果x最后一位是1,那么执行一次与运算可以把这个1消掉,但是如果最后一位是0呢??这个时候消掉的是最靠右的一个1,呵呵,可以简单的画一下看看。总之就是逐步消掉最后一个1,消了几次就有几个1啦~~ 第一个代码如下:
# include<iostream> # include<cstdio> # include<cstring> using namespace std; const int N = 101; const int M = 11; const int INF = 0xffffff; int n,m; int ct [65]; //ct[i][0]表示第i行放置炮的方案数,ct[i][j],j>0 表示第j种方案的状态 int tt[1030]; //tt[i]表示 i 表示成2进制时的1的个数,即放置炮的数目 int f [65][65]; //f[p][i][j]表示第p行取第i种方案,p-1行取第j种方案时的最大值 int hash[1030][1030]; char map [M]; int max(int x,int y){ return x>y ?x : y; } //这一行的状态 void Init_row(const int &r,int x,int key){ //r是第几行,x表示2进制的位数,key表示状态 if(x==m){ ct[r][0] ++; //ct[r][0]表示这一行状态的数目 ct[r][ct[r][0]] = key; //ct[r][i]表示第r行第i个状态 被压缩后的数字 return ; } if(map[r][x] == 'P' && (key & 1)==0 &&(key & 2)==0) //可以表示成状态 Init_row(r,x+1,(key<<1)+1); Init_row(r,x+1,key<<1); } bool Yes(int x,int y){ //判断map[x][y]能不能放置炮,会不会进入其他人射程 int xx=x,yy=y; if(hash[x][y] != -1) return hash[x][y]; while(x||y){ if((x&1)==1 && (y&1)==1){ hash[xx][yy] = false; break; } x>>=1; y>>=1; } if(hash[xx][yy] == -1) hash[xx][yy] = 1; hash[yy][xx] = hash[xx][yy]; return hash[xx][yy]; } void Dp(){ int i,j,k,p,u,v,w; for(i=1;i<=ct[1][0];i++) f[1][i][0] = tt[ct[1][i]]; //第1行放置炮的数目 if(n>=2) //第2行放置炮的数目 for(i=1;i<=ct[2][0];i++){ u=ct[2][i]; for(j=1;j<=ct[1][0];j++){ f[2][i][j] = -INF; v = ct[1][j]; if(Yes(u,v)) f[2][i][j] = max(f[2][i][j],f[1][j][0]+tt[u]); } } for(p=3;p<=n;p++){ for(k=1;k<=ct[p][0];k++){ w=ct[p][k]; for(i=1;i<=ct[p-1][0];i++){ v=ct[p-1][i]; f[p][k][i] = -INF; if(!Yes(v,w)) continue; for(j=1;j<=ct[p-2][0];j++){ u=ct[p-2][j]; if(Yes(u,v) && Yes(u,w)) f[p][k][i] = max(f[p][k][i],f[p-1][i][j] + tt[w]); } } } } } int main(){ int i,j,temp,ans; memset(hash,-1,sizeof(hash)); for(i=0;i<1030;i++){ temp = i; tt[i] = 0; while(temp){ //整数temp表示成2进制时1的个数 tt[i]++; temp &= (temp -1); } } scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%s",map[i]); for(i=1;i<=n;i++){ ct[i][0] = 0; Init_row(i,0,0); } Dp(); ans = -INF; if(n==1) for(i=1;i<=ct [0];i++) ans = max(ans,f [i][0]); else{ for(i=1;i<=ct [0];i++) for(j=1;j<=ct[n-1][0];j++) ans = max(ans,f [i][j]); } printf("%d\n",ans); return 0; }
由于M很小,最多为10,所以可以对行进行状态压缩。二进制对应位为1表示放炮兵,为0表示空。我们可以事先生成所有有效的状态,即二进制数任何两个1都要相差两位以上,同时用数组记下此状态有多少个炮兵。对于地形也进行状态压缩,用1表求高地,0表示平原。判断某个状态能否放到某个地形,就是地形状态为1的地方,放置炮兵状态一定为0,这点可以用位运算解决。判断两个状态能否放在相邻行与此相同。
代码如下:
# include <iostream> using namespace std; const int G = 70; const int N =101; const int M =11; int d[2][G][G];//滚动数组 int ph ,f[G];//ph数组用于判断状态在第i行是否满足在P的平原设置,f数组则存放满足的状态 int n,m,g;//g为所有状态数 int OneC(int x){//计算x二进制1的个数,这个算法很优秀,是在《编程之美》书中学到的。 int t =0; while(x){ t ++; x &= (x-1); } return t; } void DP(){//dp for(int k =2; k < n ;k++){ for(int i =0; i< g; i++){ if(ph[k] != (ph[k] | f[i]))continue;//判断状态f[i]是否满足在平原设置炮台 for(int j = 0;j < g; j++){ if(ph[k-1] != (ph[k-1] | f[j]))continue; if(f[i] & f[j])continue;//判断第k行和第k-1行的炮台是否有彼此击中 for(int q=0; q< g; q ++){ if(ph[k-2] != (ph[k-2] | f[q]))continue; if(f[q] & f[j])continue; if(f[i] & f[q])continue; d[k%2][i][j] = max(d[k%2][i][j], d[(k+1)%2][j][q] + OneC(f[i])); //状态方程 } } } } } int main(){ scanf("%d %d",&n,&m); int i ,j; char ch[M]; for(i =0;i < n; i++){//计算ph[] ph[i] =0; scanf("%s", ch); for(j =0; j < m; j++){ if(ch[j] == 'P') ph[i] += (1<<(m-j-1)); } } int v = 1<<m; for(i =0,g =0; i< v;i++){//挑选合法状态 if(((i & (i << 2)) == 0) && (i & (i <<1))==0)//这个想法不错哦。 f[g++] = i; } int pMax ; if(n ==1){ pMax = 0; for(i =0; i<g ; i++){ if((ph[0] | f[i])==ph[0]) pMax = max( pMax , OneC(f[i])); } printf("%d" ,pMax); return 0; } memset(d, 0,sizeof(d)); pMax = 0; for(i =0; i < g; i++){//初始化d if(ph[1] != (ph[1] | f[i]))continue; for(j =0 ;j < g; j++){ if(ph[0] != (ph[0] | f[j]))continue; if((f[i] & f[j]) ==0){ d[1][i][j] = OneC(f[i]) + OneC(f[j]); pMax = max (pMax , d[1][i][j]); } } } if(n ==2){ printf("%d" ,pMax); return 0; } DP(); for(i =0; i < g; i ++){ for(j =0; j< g; j++){ pMax = max( pMax ,d[(n+1)%2][i][j]); } } printf("%d", pMax); return 0; }
代码如下:
# include<cstdio> # include<string> # include<cstring> # include<iostream> # include<cmath> # include<algorithm> using namespace std; int n,m,sum,num,sta[1<<11],cot[1<<11],dp[105][105][105],a[105]; bool fit(int x,int y) { if(x&y) return 0; return 1; } void init() { sum=1<<m;num=0; for(int i=0;i<sum;i++) { if(i&(i<<1)||i&(i<<2)) continue; sta[num]=i; int temp = i,count=0; while(temp) { count++; temp&=temp-1; } cot[num++]=count; } } void DP() { int ans=0; for(int i=0;i<num;i++) { if(!fit(a[1],sta[i])) continue; dp[1][0][i] = cot[i]; if(ans<dp[1][0][i]) ans=dp[1][0][i]; } for(int i=2;i<=n;i++) for(int j=0;j<num;j++) for(int k=0;k<num;k++) { if(!fit(sta[k],sta[j])||!fit(a[i],sta[k])||!fit(a[i-1],sta[j])) continue; for(int l=0;l<num;l++) { if(!fit(sta[k],sta[l])||!fit(sta[j],sta[l])||!fit(a[i-2],sta[l])||!dp[i-1][l][j]) continue; dp[i][j][k]=max(dp[i][j][k],dp[i-1][l][j]+cot[k]); if(ans<dp[i][j][k]) ans=dp[i][j][k]; } } printf("%d\n",ans); } int main() { char s; scanf("%d%d",&n,&m); getchar(); init(); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { scanf("%c",&s); if(s=='H') { int tem=1<<(j-1); a[i]+=tem; } } getchar(); } DP(); return 0; }
另附转的一篇/article/6608884.html
相关文章推荐
- 炮兵阵地(poj1185,状态压缩的动态规划)
- Poj-1185 & Nyoj-81 炮兵阵地 (状态压缩动态规划经典题
- 100道动态规划——27 POJ 1185 炮兵阵地 状态压缩,预处理,滚动数组
- poj 1185 炮兵阵地 状态压缩+dp sum dp,s,Map
- poj1185炮兵阵地(状态压缩DP)
- POJ 1185 炮兵阵地 (状态压缩dp)
- poj 1185 && NYOJ 85 炮兵阵地(状态压缩dp)
- 炮兵阵地 POJ - 1185(状态压缩)
- 状态压缩DP-炮兵阵地(POJ 1185)
- POJ 1185 炮兵阵地 (经典状态压缩题)
- POJ 1185 炮兵阵地 状态压缩DP
- POJ_1185_炮兵阵地 dp+状态压缩
- poj1185 炮兵阵地 经典状态压缩dp
- POJ1185 炮兵阵地 状态压缩DP
- poj 1185 炮兵阵地(三维状态压缩dP)
- POJ 炮兵阵地 1185 状态压缩dp
- 状态压缩dp_POJ1185炮兵阵地
- poj 1185 炮兵阵地 状态压缩dp
- 状态压缩 -- POJ 1185 炮兵阵地【状态压缩DP】
- POJ 1185 炮兵阵地(状态压缩DP)