码迷,mamicode.com
首页 > 其他好文 > 详细

POJ 1185 - 炮兵阵地 - [状压DP]

时间:2017-09-27 23:54:07      阅读:288      评论:0      收藏:0      [点我收藏+]

标签:等于   esc   code   包含   img   www   使用   using   es2017   

 

印象中这道题好像我曾经肝过,但是没肝出来,现在肝出来了也挺开心的技术分享

                                                                                                                                                                                                                   

题目链接:http://poj.org/problem?id=1185

Time Limit: 2000MS Memory Limit: 65536K

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://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 }

 

POJ 1185 - 炮兵阵地 - [状压DP]

标签:等于   esc   code   包含   img   www   使用   using   es2017   

原文地址:http://www.cnblogs.com/dilthey/p/7604432.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!