[关闭]
@Chilling 2017-03-04T10:07:43.000000Z 字数 2120 阅读 944

POJ-1185: 炮兵阵地

状压DP


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

分析:每个格子对应的状态有两种,放炮兵和不放炮兵,因此这道题我们可以用状态压缩DP来做。由于m为列数,是小于等于10的,因此初步看起来,有$2^{10}种状态,但是其实并不是这样的,因为题目有限制,保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内,那么状态就缩小到了不到70种。我们可以预先处理出可行的状态,并且统计出每种有效状态中1的个数(即放置炮兵的个数)。

接下来就是dp的环节了。对于数组dp[i][j][k],我们表示第i行的状态为st[j],第i-1行的状态为st[k]时候的最优解(st数组表示有效的放置)。
那么第i行的状态可以由上一行的状态推出,则有状态转移方程:


  1. #include<stdio.h>
  2. #include<algorithm>
  3. #include<string.h>
  4. using namespace std;
  5. char mp[111][11];//从mp[1]开始
  6. int st[70],cnt; //有效状态,个数
  7. int num[70];//每个有效状态中1的个数
  8. int now[111];//现在每一行的状态用二进制表示,从1开始
  9. int dp[111][70][70];//第i行状态为st[j],i-1行状态为st[k]的最优解
  10. int n,m;
  11. int one(int i) //统计1的个数
  12. {
  13. int n=0;
  14. while(i)
  15. {
  16. if(i%2==1)
  17. n++;
  18. i/=2;
  19. }
  20. return n;
  21. }
  22. void valid() //有效状态
  23. {
  24. int mask=(1<<m);
  25. for(int i=0;i<mask;i++)
  26. {
  27. if((!(i&(i<<1)))&&(!(i&(i<<2)))) //i的左右各两个都为0,合法
  28. {
  29. st[cnt]=i;
  30. num[cnt++]=one(i);
  31. }
  32. }
  33. }
  34. int main()
  35. {
  36. int i,j,k,kk,ans;
  37. while(scanf("%d%d",&n,&m)!=EOF)
  38. {
  39. memset(now,0,sizeof(now));
  40. memset(dp,0,sizeof(dp));
  41. memset(num,0,sizeof(num));
  42. memset(st,0,sizeof(st));
  43. ans=0,cnt=0;
  44. valid();
  45. for(i=1;i<=n;i++) //从第二行开始,第一行空出来,now[0]就全为0,当作全是空地
  46. {
  47. scanf("%s",mp[i]);
  48. for(j=0;j<m;j++)
  49. if(mp[i][j]=='H')
  50. now[i]+=(1<<(m-j-1)); //处理输入的地图为n行二进制数
  51. }
  52. /* for(i=1;i<=n;i++)
  53. printf("%d\n",now[i]);*/
  54. for(i=0;i<cnt;i++)
  55. {
  56. if(!(now[1]&st[i]))//不冲突
  57. {
  58. dp[1][i][0]=num[i];
  59. ans=max(ans,num[i]);
  60. // printf("%d %d\n",ans,st[i]);
  61. }
  62. }
  63. for(i=2;i<=n;i++)
  64. {
  65. for(j=0;j<cnt;j++)//这一行
  66. {
  67. if(!(now[i]&st[j]))//这行和可行状态不冲突,填充
  68. for(k=0;k<cnt;k++)
  69. {
  70. if(!(st[j]&st[k]))//上行和这行不冲突
  71. for(kk=0;kk<cnt;kk++)
  72. if((!(st[kk]&st[k]))&&(!(st[kk]&st[j])))//上行和上上行不冲突,这行也和上上行不冲突
  73. dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][kk]+num[j]);
  74. ans=max(ans,dp[i][j][k]);
  75. }
  76. }
  77. }
  78. printf("%d\n",ans);
  79. }
  80. return 0;
  81. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注