POJ 1185 - 炮兵阵地 & HDU 4539 - 郑厂长系列故事——排兵布阵 - [状压DP]
印象中这道题好像我曾经肝过,但是没肝出来,现在肝出来了也挺开心的
题目链接:http://poj.org/problem?id=1185
Time Limit: 2000MS Memory Limit: 65536K
Description
![](http://poj.org/images/1185_1.jpg)
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
Input
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
Output
Sample Input
5 4 PHPP PPHH PPPP PHPP PHHP
Sample Output
6
题解:
感谢博文http://www.cnblogs.com/scau20110726/archive/2013/02/27/2935256.html,可以说讲的非常清晰了;
首先,一个炮的攻击范围有两格,所以对于第i行来讲,i-1行和i-2行对它有影响,i-3行及以上的都没有影响了;
所以我们要去求得到关于第i行的一些信息,只需要知道i-1和i-2的信息即可;
然后考虑表示地图:
山用1表示,空地用0表示;那么对于一行,就是一个0 or 1的串,这是个二进制数,可以状压成一个十进制数;
再考虑表示一行上部署炮兵部队的状态:
只考虑一行上,能不能放炮的话,状态最大达到 (1<<10) - 1 = 2^10 -1;
但是实际上,因为炮与炮之间不能相互攻击的限制,状态数没有那么多;
假设一个状态i,如果满足 (i&(i<<1)) == 0 && (i&(i<<2)) == 0 的话,它才是一个符合炮与炮之间相互不能攻击的状态;
由此,我们通过枚举计算一下到底有多少状态:
for(int i=0;i<=(1<<10)-1;i++) if( (i&(i<<1))==0 && (i&(i<<2))==0 ) cnt++;
程序运行得到的cnt等于60,所以我们可以知道,最大的状态数不会超过60;
由此使用一个state[]数组保存状态(依然状压成十进制数),把每次枚举出来可行的状态保存进去;
并且值得一提的是,我们把state定义成结构体数组:
state[i].sta:表示第i个状态时怎么样的;
state[i].cnt:记录这个状态下,部署了多少炮兵部队;
这样一来,方便后续操作,也不容易出错,在枚举求得所有可行状态时,也可以一并求出cnt;
那么怎么判断炮兵部队不在山上呢? 只要state[i] & mp[r] == 0 ,就表示state[i]这个状态,可以放在r这行上,而且炮不会在山上,炮之间也不会攻击;
那么又如何判断 i行,i-1行,i-2行的炮没有冲突呢?所以我们假设现在i行,i-1行,i-2行的炮的摆放情况分别是state[i],state[j],state[k];
当满足 state[i] & state[j] == 0 state[i] & state[k] == 0 state[j] & state[k] == 0 条件时,三行的部署情况没有冲突;
最后的状态转移方程,可以直接参考代码中的状态转移过程;
AC代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define MAXN 103 5 #define MAXM 11 6 using namespace std; 7 int n,m; 8 int mp[MAXN]; 9 10 int state_num; 11 struct State{ 12 int sta,cnt; 13 }state[63]; 14 15 int dp[MAXN][63][63]; 16 17 int main() 18 { 19 scanf("%d%d",&n,&m); 20 21 for(int i=1;i<=n;i++) 22 { 23 mp[i]=0; 24 char input[12]; 25 scanf("%s",input+1); 26 for(int j=1;j<=m;j++) 27 { 28 int tmp=((input[j]=='H')?1:0); 29 mp[i]|=tmp; 30 if(j!=m) mp[i]=mp[i]<<1; 31 } 32 }//二进制记录地图 33 34 state_num=0; 35 for(int i=0;i<=(1<<m)-1;i++) 36 { 37 if( (i&(i<<1))==0 && (i&(i<<2))==0 ) 38 { 39 state_num++; 40 state[state_num].sta=i; 41 42 state[state_num].cnt=0; 43 for(int tmp=i;tmp;tmp=(tmp>>1)) if(tmp&1) state[state_num].cnt++; 44 45 //printf("id=%d state=%d cnt=%d\n",state_num,state[state_num].sta,state[state_num].cnt); 46 } 47 }//单纯在炮与炮之间不能互相攻击的限制下,得到所有状态 48 49 //状态转移过程 - st 50 memset(dp,0,sizeof(dp)); 51 52 for(int i=1;i<=state_num;i++) 53 { 54 if(state[i].sta & mp[1]) continue; 55 dp[1][i][1]=state[i].cnt; 56 }//初始化dp[1][ state of row1 ][ no state ] 57 58 for(int i=1;i<=state_num;i++)//枚举第2行状态 59 { 60 if(state[i].sta & mp[2]) continue; 61 for(int j=1;j<=state_num;j++)//枚举第1行状态 62 { 63 if( (state[j].sta&mp[1]) || (state[i].sta&state[j].sta) ) continue; 64 dp[2][i][j]=max(dp[2][i][j],dp[1][j][1]+state[i].cnt); 65 } 66 }//初始化dp[2][ state of row2 ][ state of row1 ] 67 68 for(int r=3;r<=n;r++) 69 { 70 71 for(int i=1;i<=state_num;i++)//枚举第r行状态 72 { 73 74 if(state[i].sta & mp[r]) continue; 75 76 for(int j=1;j<=state_num;j++)//枚举第r-1行状态 77 { 78 79 if( (state[j].sta&mp[r-1]) || (state[i].sta&state[j].sta) ) continue; 80 81 for(int k=1;k<=state_num;k++)//枚举第r-2行状态 82 { 83 if(state[k].sta & mp[r-2]) continue; 84 if( (state[i].sta&state[k].sta) || (state[j].sta&state[k].sta) ) continue; 85 86 dp[r][i][j]=max(dp[r][i][j],dp[r-1][j][k]+state[i].cnt); 87 } 88 89 } 90 91 } 92 93 } 94 //状态转移过程 - ed 95 96 int ans=0; 97 for(int i=1;i<=state_num;i++) 98 { 99 for(int j=1;j<=state_num;j++) 100 { 101 ans=max(ans,dp[n][i][j]); 102 } 103 } 104 printf("%d\n",ans); 105 }
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4539
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)
也不是副厂长
他根本就不是厂长
事实上
他是带兵打仗的团长
一天,郑厂长带着他的军队来到了一个n*m的平原准备布阵。
根据以往的战斗经验,每个士兵可以攻击到并且只能攻击到与之曼哈顿距离为2的位置以及士兵本身所在的位置。当然,一个士兵不能站在另外一个士兵所能攻击到的位置,同时因为地形的原因平原上也不是每一个位置都可以安排士兵。
现在,已知n,m 以及平原阵地的具体地形,请你帮助郑厂长计算该阵地,最多能安排多少个士兵。
每组数据的第一行包含2个整数n和m (n <= 100, m <= 10 ),之间用空格隔开;
接下来的n行,每行m个数,表示n*m的矩形阵地,其中1表示该位置可以安排士兵,0表示该地形不允许安排士兵。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define MAXN 103 5 #define MAXM 11 6 using namespace std; 7 int n,m; 8 int mp[MAXN]; 9 10 int state_num; 11 struct State{ 12 int sta,cnt; 13 }state[173]; 14 15 int dp[MAXN][173][173]; 16 17 int main() 18 { 19 while(scanf("%d%d",&n,&m)!=EOF) 20 { 21 for(int i=1,tmp;i<=n;i++) 22 { 23 mp[i]=0; 24 for(int j=1;j<=m;j++) 25 { 26 scanf("%d",&tmp); 27 tmp=!tmp; 28 mp[i]|=tmp; 29 if(j!=m) mp[i]=mp[i]<<1; 30 } 31 }//二进制记录地图 32 33 state_num=0; 34 for(int i=0;i<=(1<<m)-1;i++) 35 { 36 if( (i&(i<<2))==0 ) 37 { 38 state[state_num].sta=i; 39 40 state[state_num].cnt=0; 41 for(int tmp=i;tmp;tmp=(tmp>>1)) if(tmp&1) state[state_num].cnt++; 42 43 //printf("id=%d state=%d cnt=%d\n",state_num,state[state_num].sta,state[state_num].cnt); 44 state_num++; 45 } 46 }//单纯在士兵与士兵之间不能互相攻击的限制下,得到所有状态 47 48 //状态转移过程 - st 49 memset(dp,0,sizeof(dp)); 50 51 for(int i=0;i<state_num;i++) 52 { 53 if(state[i].sta & mp[1]) continue; 54 dp[1][i][0]=state[i].cnt; 55 }//初始化dp[1][ state of row1 ][ no state ] 56 57 for(int i=0;i<state_num;i++)//枚举第2行状态 58 { 59 if(state[i].sta & mp[2]) continue; 60 for(int j=0;j<state_num;j++)//枚举第1行状态 61 { 62 if(state[j].sta & mp[1]) continue; 63 if((state[j].sta<<1)&state[i].sta || (state[j].sta>>1)&state[i].sta) continue; 64 dp[2][i][j]=max(dp[2][i][j],dp[1][j][0]+state[i].cnt); 65 } 66 }//初始化dp[2][ state of row2 ][ state of row1 ] 67 68 for(int r=3;r<=n;r++) 69 { 70 71 for(int i=0;i<state_num;i++)//枚举第r行状态 72 { 73 74 if(state[i].sta & mp[r]) continue; 75 76 for(int j=0;j<state_num;j++)//枚举第r-1行状态 77 { 78 79 if(state[j].sta & mp[r-1]) continue; 80 if((state[j].sta<<1)&state[i].sta || (state[j].sta>>1)&state[i].sta) continue; 81 for(int k=0;k<state_num;k++)//枚举第r-2行状态 82 { 83 if(state[k].sta & mp[r-2]) continue; 84 if(state[i].sta & state[k].sta) continue; 85 if((state[k].sta<<1)&state[j].sta || (state[k].sta>>1)&state[j].sta) continue; 86 87 dp[r][i][j]=max(dp[r][i][j],dp[r-1][j][k]+state[i].cnt); 88 } 89 90 } 91 92 } 93 94 } 95 //状态转移过程 - ed 96 97 int ans=0; 98 for(int i=0;i<state_num;i++) 99 { 100 for(int j=0;j<state_num;j++) 101 { 102 ans=max(ans,dp[n][i][j]); 103 } 104 } 105 printf("%d\n",ans); 106 } 107 }