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

来源:互联网 发布:金山tw域名遭抢注 编辑:程序博客网 时间:2024/06/08 19:26
炮兵阵地

Description

司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H"表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
ACM: <wbr>动态规划题 <wbr>poj <wbr>1185 <wbr>想了两天,预处理很重要.

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

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[n][i][j] = max(dp[n][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];
intdp[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;
}

0 0