【状态压缩动态规划】炮兵阵地 pas…

炮兵阵地【状态压缩动态规划】

Time Limit:1000MS  Memory Limit:65536K
Total Submit:66 Accepted:45

Description

炮兵阵地(cannon.pas/c/cpp)


【问题描述】

  司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:


【状态压缩动态规划】炮兵阵地 <wbr>pascal <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

Hint

状态压缩经典案例

Source

NOI2001

 

 

对于一个地点能否放置炮兵,要考虑

(1)这个地方的地形

(2)前一列的摆放情况

(3)前两列的摆放情况

(4)前一行的摆放情况

(5)前两行的摆放情况

于是我们就先把前三个条件预处理,用深搜枚举出每一行可以放置的所有可能。

然后进行dp,具体见程序

 

var
 n,m:integer;
 st:string;
 map:array[1..100]of string[15];
 state:array[1..100,1..60]of longint;//state[s,i]表示第s行的第i种可能
 sum:array[1..100,0..60]of integer;//sum[s,i]表示第s行第i种可能中炮兵数,sum[s,0]表示第s行的可能总数
 f:array[1..100,1..60,1..60]of integer;//动归用的数组,f[s,i,j]表示 第s行取第i种可能,第s-1行取第j种可能时,能放的最大炮兵数

function jinzhi(st:string):longint;//把十进制转为二进制,方便位运算
var
 i,k,s:longint;
begin
 k:=1;
 s:=0;
 for i:=length(st) downto 1 do
  begin
  s:=s+(ord(st[i])-ord('0'))*k;
  k:=k*2;
  end;
  jinzhi:=s;
end;

procedure dfs(st:string;s,i:longint);//用深搜枚举出每一行可以放置的所有可能
var
 i1,i2,total:longint;
 st1:string;
begin
  if i>m+1 then exit;
  inc(sum[s,0]);
  state[s,sum[s,0]]:=jinzhi(st);
  total:=0;
  for i1:=1 to m do
   if st[i1]='1' then inc(total);
  sum[s,sum[s,0]]:=total;

  for i1:=i to m do
   if (map[s,i1]='P')and((st[i1-2]='0')or(i1<3))and((st[i1-1]='0')or(i1<2)) then
    begin
    st1:=st;
    st1[i1]:='1';
    dfs(st1,s,i1+1);
    end;
end;

function check(s,i,j,p:longint):boolean;//判断第s行第i种情况与第s-p行第j种情况是否矛盾
var
 i1:longint;
begin
 if (state[s,i])and(state[s-p,j])<>0 then exit(false);
 exit(true);
end;

procedure init;
var
 i,j:longint;
begin
 readln(n,m);
 for i:=1 to n do
  readln(map[i]);

 for i:=1 to n do
  begin
  st:='';
  for j:=1 to m do st:=st+'0';
  dfs(st,i,1)
  end;
end;


procedure main;
var
 i,j,k,s:longint;
begin
  for i:=1 to sum[2,0] do
   for j:=1 to sum[1,0] do
    if check(2,i,j,1) then f[2,i,j]:=sum[2,i]+sum[1,j];

//当 行数<3 时,不用考虑前面行的情况,直接把炮兵数相加

 for s:=3 to n do//对于一行
  for i:=1 to sum[s,0] do//取第i种情况
   for j:=1 to sum[s-1,0] do//第s-1行取第j种情况
   begin
   if check(s,i,j,1) then
    for k:=1 to sum[s-2,0] do//第s-2行取第k种情况
    if check(s,i,k,2) then
     if f[s-1,j,k]+sum[s,i]>f[s,i,j] then
        f[s,i,j]:=f[s-1,j,k]+sum[s,i];//取最大值
   end;

end;

procedure print;
var
 i,j,max:longint;
begin
 max:=0;
 for i:=1 to sum[n,0] do
  for j:=1 to sum[n-1,0] do
  if f[n,i,j]>max then max:=f[n,i,j];
 write(max);
end;


begin
 init;
 main;
 print;
end.

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值