NOI2001《炮兵阵地》 状压DP

炮兵阵地

Time Limit:1000MS  Memory Limit:65536K
Total Submit:30 Accepted:14

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


题目大意:

  给出一个N*M的图,图上有一些点可以放棋子,有一些不能。每个棋子的上下左右相邻2个格子内不能放置其他棋子,问最多能放几个棋子。

题解:

  状态压缩DP

  每一行棋子放置的状态我们可以用一个二进制数表示,那么每一行能放置的所有状态可以预处理得出的。设S[i]为一行中可放置的第i种状态(用DFS求出)

  设F[i,j,k]为第i行的状态为第j个状态,i-1行为第k个状态时能放置的最大数量,易得出F[i,j,k]=max(f[i-1,k,l])+c[j] |s[j] s[k] s[l] 不冲突|

                                                                                  C[i]表示第i种状态中1的个数(即放置炮兵的个数)

  在DP过程中要判断两个状态不冲突,实际上就是s[i] and s[j]=0,要注意的是图中有一些点不能放炮兵,在读入时要处理出每一行不能放的状态,在判断时使其不与当前行状态冲突。


const
  maxn=100;
  maxm=100;
var
  f:array[0..maxn,0..maxm,0..maxm]of longint;
  a:array[-2..maxn]of longint;
  s,c:array[0..maxm]of longint;
  b:array[0..10]of longint;
  n,m,sum,ans,p:longint;
  i,j,k,l:longint;

procedure init;
var
  i,j:longint;
  ch:char;
begin
  readln(n,m);
  for i:=1 to n do
  begin
    for j:=1 to m do
    begin
      read(ch);
      if ch='P' then a[i]:=a[i]*2 else if ch='H' then a[i]:=a[i]*2+1;
    end;
    readln;
  end;
  sum:=0;
end;

function max(a,b:longint):longint;
begin
  if a>b then exit(a) else exit(b);
end;

procedure dfs(dep:longint);
var
  i:longint;
begin
  if dep>m then
  begin
    inc(sum);s[sum]:=0;
    for i:=1 to m do
    begin
      s[sum]:=s[sum]*2+b[i];
      inc(c[sum],b[i]);
    end;
    exit;
  end;
  dfs(dep+1);
  b[dep]:=1;
  dfs(dep+3);
  b[dep]:=0;
end;

begin
  init;
  dfs(1);
  for i:=1 to n do
    for j:=1 to sum do
      for k:=1 to sum do
      begin
        for l:=1 to sum do
          if (s[j] and s[k])+(s[j] and s[l])+(s[k] and s[l])+(s[j] and a[i])+(s[k] and a[i-1])+(s[l] and a[i-2])=0 then
            f[i,j,k]:=max(f[i,j,k],f[i-1,k,l]);

       f[i,j,k]:=f[i,j,k]+c[j];
      end;
  for i:=1 to sum do
    for j:=1 to sum do
      if f[n,i,j]>ans then ans:=f[n,i,j];
  writeln(ans);
end.



  

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值