NOI2001 炮兵阵地详解
【题目描述】
司令部的将军们打算在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
【题目解析】
对于一个位置,如果它部署了一支部队,那么会对它的前后左右的2格位置产生影响,如果以行作为状态,则不能单单由上一状态转移,那样的话不能保证在它的2格处不发生矛盾,而在这个题目中,以行做状态又很明显,所以在状态的转移时必须是上一行的状态和上上行的状态一起转移,如果用f[i,j,k]来表示在第i行时,取第j个状态(注意是第j个状态),i-1行取的是第k个状态,那么不难得出状态转移方程:
f[i,j,k]=max{f[i,j,k],f[i-1,k,l]+ff[i,j]}
解释一下这个方程i,j,k的含义如上述,l表示在第i-2行取第l个状态,ff[i,j]表示在第i行取第j个状态时在该行放置的多少支部队,由于第二行仅与第一行有关,所以要对第二行进行一下特殊处理,对于第二行,我们有
f[2,j,k]=max{ff[2,j]+ff[1,k]};
这样一来,对动态规划的模型基本处理就完成了,同时我们发现,在m=10时,每一行的状态有2^10种,显然时间和空间都不能承受,必须做出优化,仔细阅读题目,发现如果一个点部署了部队,则其前后两格范围内均不能再部署部队,而且在一个格子为H时,它根本不能部署部队,因此,在许多的状态中,有许多状态是自相矛盾的,根本不需要考虑,可以用某种预处理,把状态提取出来,仅仅储存有用的状态,在这里我的方法是用深度优先搜索预处理一下,try(x:longint)为处理某一行的第x个位置,如果这个位置部署部队,那么x+1,x+2这两个位置都不能放,直接try(x+3)即可,而如果这个地方是山地,就不能放,则try(x+1),即使是平原,也可以不去部署部队,因此还原后也要try(x+1),预处理结束。
对于状态的储存,为了简洁,用一个二进制数来表示,如当m=4时有一种状态为放,不放,不放,放,就用1代表放,0代表不放,这个二进制数为1001,转化为十进制数为9.这样状态的储存问题就迎刃而解了。
接下来,判断状态的矛盾问题,这用到了位运算的知识,1001and0011=0001 这已经很明了了,判断两行间的状态是否矛盾,只需要用(状态1)and(状态2),如果结果是0,说明两个状态不矛盾。
1 program connon(input,output);
2 var g:array[1..100,1..100] of longint;
3 ff:array[1..100,1..100] of longint;
4 f:array[1..100,1..100,1..100] of longint;
5 a:array[1..100,1..10] of char;
6 d:array[1..100] of longint;
7 i,j,k,l,maxn,m,n,tot,sum,ans:longint;
8 procedure init;
9 begin
10 readln(n,m);
11 for i:=1 to n do
12 begin
13 for j:=1 to m do
14 ead(a[i,j]);
15 readln;
16 end;
17 fillchar(f,sizeof(f),0);
18 end;
19 function max(x,y:longint):longint;
20 begin
21 if x>y then
22 exit(x)
23 else
24 exit(y);
25 end;
26 procedure dfs(x:longint);
27 begin
28 if x>m then
29 begin
30 inc(tot);
31 g[i,tot]:=sum;
32 ff[i,tot]:=ans;
33 d[i]:=tot;
34 exit;
35 end;
36 if a[i,x]='P' then
37 begin
38 sum:=sum+1<<(x-1);
39 inc(ans);
40 dfs(x+3);
41 sum:=sum-1<<(x-1);
42 dec(ans);
43 end;
44 dfs(x+1);
45 end;
46 procedure make;
47 begin
48 for i:=1 to d[2] do
49 for j:=1 to d[1] do
50 if (g[1,j] and g[2,i]=0) then
51 f[2,i,j]:=ff[2,i]+ff[1,j];
52 end;
53 procedure dp;
54 begin
55 for i:=3 to n do
56 for j:=1 to d[i] do
57 for k:=1 to d[i-1] do
58 for l:=1 to d[i-2] do
59 if (g[i,j] and g[i-1,k]=0) then
60 if (g[i,j] and g[i-2,l]=0) then
61 if (g[i-1,k] and g[i-2,l]=0) then
62 f[i,j,k]:=max(f[i,j,k],f[i-1,k,l]+ff[i,j]);
63 end;
64 procedure choose;
65 var i,j,k:longint;
66 begin
67 maxn:=-(maxlongint>>1);
68 for i:=1 to d[n] do
69 for j:=1 to d[n-1] do
70 if f[n,i,j]>maxn then
71 maxn:=f[n,i,j];
72 end;
73 procedure print;
74 begin
75 writeln(maxn);
76 end;
77 begin
78 assign(input,'connon.in');reset(input);
79 assign(output,'connon.out');rewrite(output);
80 init;
81 for i:=1 to n do
82 begin
83 tot:=0;
84 sum:=0;
85 ans:=0;
86 dfs(1);
87 end;
88 make;
89 dp;
90 choose;
91 print;
92 close(input);
93 close(output);
94 end.