您的位置:首页 > 其它

ACM: 动态规划题 poj 1185 想了两…

2016-05-19 23:25 337 查看
[align=center]炮兵阵地[/align]
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

 

题意: 给定一块地形, 包括山丘'H'和平原'P'两种, 平地可以放置炮兵, 每个炮兵的攻击范围

     
是一个左右各2个格和上下各2个格. 要求炮兵相互不能攻击, 计算最大放置炮兵数.

     
(山地不可以放置, 平原可以).

 

解题思路:

     1.
题意很清晰, 设状态: dp[i][j][k]表示: 当前第i层状态为j, 第i-1层状态为k,

                   
从0层~i层的在当前状态的炮兵个数.(依然采用状态压缩方法)

    
2. 动态方程: dp[i][j][k] = (dp[i-1][k][t]+当前层(第i层)的炮兵数) (t是第i-2层状态)

        一开始想用递归方法处理DP(d,
i, j, k, count, x, y)//d列,i当前层状态, j上一层状态

       
k是上一层的上一层状态, count计数炮兵, (x,y)坐标用来判断山地和平原.

       
DP(d, i, j, k, count, x, y)

       
{

          
if(d == m) //最后一列

             
dp[i][j][k] = (dp[i-1][k][t]+count);

             
return;

          
if(d+1 <= m) ....

          
if(d+3 <= m) ....

       
}

       
发现处理边界时候很复杂. 并且每行的状态2^10, 空间复杂度O(100*2^10*1^10)非常大.

       
这样处理不实际, 这里纠结了好久.

     3.
接着想怎么可以将状态减少, 发现因为炮兵的横向攻击范围是左右各2个格, 所有一半的

        状态时无效的,
空间复杂度降低到O(100*2^6*2*6). 既然每行可行的状态不多, 可以保存

       
行的可行解作为预处理(关键).

       
for(i = 0; i < (1<<m);
++i)

           
if( !( (i &
(i<<1)) || (i
& (i<<2))
) )

               
i就是一个可行解.

    
4. 有了预处理, 用递推方法就方便多了;

    
for(n = 1; n < N; ++n)

        for(i
= 0; i < num; ++i) //当前n行的状态

          
for(j = 0; j < num; ++j) //n-1行状态

              
for(k = 0; k < num; ++k)
//n-2行状态

                 
dp
[i][j] = max(dp
[i][j], dp[n-1][j][k]+状态i的炮兵数);

 

代码:

#include <cstdio>

#include <iostream>

#include <cstring>

using namespace std;

#define MAX 105

#define MAXSIZE 13

 

int n, m;

int g[MAX];

int
dp[MAX][1<<(MAXSIZE/2)][1<<(MAXSIZE/2)];

int line[MAX], sum[MAX], num; //line保存可行解, sum保存可行解的炮兵数目

 

inline bool cmp(int x)

{

 if( x &
(x<<1) ) return false;

 if( x &
(x<<2) ) return false;

 return true;

}

 

inline int max(int a, int b)

{

 return a > b ? a : b;

}

 

inline int getSum(int x)

{

 int sum = 0;

 while(x > 0)

 {

  if(x & 1)
sum++;

  x
>>= 1;

 }

 return sum;

}

 

void init()

{

 num = 0;

 for(int i = 0; i <
(1<<m); ++i)

 {

  if( cmp(i) )

  {

   line[num] =
i;

   sum[num++] =
getSum(i);

  }

 }

}

 

int DP()

{

 int i,j, k, t;

 for(i = 0; i < num; ++i)

 {

  if( !(line[i] &
g[0]) )

   dp[0][i][0] =
sum[i];

 }

 for(i = 1; i < n; ++i)

 {

  for(j = 0; j <
num; ++j) //i行

  {

   if(g[i]
&  line[j]) continue; //g是H=1,P=0.
g和line相同时为假(匹配地形)

   for(k = 0; k
< num; ++k) // i-1行

   {

    if(line[j]
& line[k]) continue;

    for(t
= 0; t < num; ++t) //i-2行

    {

     if(line[k]
& line[t]) continue;

     if(line[j]
& line[t]) continue;

     if(dp[i-1][k][t]
== -1) continue;

     dp[i][j][k]
= max(dp[i][j][k], dp[i-1][k][t]+sum[j]);

    }

   }

  }

 }

 int result = 0;

 for(i = 0; i < num; ++i)

 {

  for(j = 0; j <
num; ++j)

   result =
max(result, dp[n-1][i][j]);

 }

 return result;

}

 

int main()

{

 int i, j;

 char ch;

// freopen("input.txt", "r", stdin);

 while(scanf("%d %d",&n,
&m) != EOF)

 {

  memset(dp, -1,
sizeof(dp));

  memset(g, 0, sizeof(g));

  for(i = 0; i <
n; ++i)

  {

   getchar();

   for(j = 0; j
< m; ++j)

   {

    scanf("%c",&ch);

    if(ch
== 'H') g[i] |= (1<<j);

   }

  }

  init();

  printf("%d\n", DP());

 }

 return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: