NOI2001炮兵阵地

NOI2001炮兵阵地解题报告

【问题描述】 

    司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由NM列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示)。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);炮兵能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格,不受地形影响。

    现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

    n<=100 m<=10

【分析】

    看到这道题,很容易想到八皇后问题,那题是npc问题,不能在多项式时间内求解,但是这两题是有差别的。这题中,炮兵阵地的攻击范围只有两格,也就是当前位置是否能放置只和前两行两列有关,是可以用动规解决的。

    因为当前位置能否放置只和前两行两列有关,不难得出状态,f[i,k1,k2,k3]表示前i行,第i行放置情况为k1,第i-1行放置情况为k2,第i-2行放置情况为k3最多放的炮兵数。但其实我们可以发现其实不需要三行状态,只需要i和i-1行就可以了。因为三个状态我们要一个循环来枚举前一个状态,但是如果只有两行状态,我们也用一个循环枚举前一个状态,同时也可以得知i-2行的情况,因此可以降一维。

    f[i,k1,k2]表示第i行放置情况为k1,i-1行为k2,前i行最多放置数。显然f[i,k1,k2]:=max(f[i-1,k2,k3]+b[k1]);

b[k1]表示该行状态为k1的放置数。

    那我们怎么表示状态呢?我们发现,m很小,最多只有10,2^10=1024,我们可以用一个二进制数表示当前行的状态,第i位如果为0表示不放,1表示放。但是1024还是很大,时间空间都不允许,因此要进行状态压缩。当m=10时,虽然可能有1024种状态,但其实很多状态是明显不符合的,可以预处理去掉,符合的状态其实只有60种,具体见程序。

    然后配上各种神奇的位运算,可以秒过此题。

【代码】

    

View Code
 1 var i,j,n,m,k,l:longint;
2 a,b:array[0..1200]of longint;
3 now:array[0..100]of longint;
4 s:string;
5 f:array[0..100,0..60,0..60]of longint;
6 bo:boolean;
7 function max(i,j:longint):longint;
8 begin
9 if i<j then exit(j) else exit(i);
10 end;
11
12 function getsum(i:longint):longint;//平行算法,求二进制数中1的个数
13 begin
14 i:=(i and $55555555)+((i shr 1)and $55555555);
15 i:=(i and $33333333)+((i shr 2)and $33333333);
16 i:=(i and $0f0f0f0f)+((i shr 4)and $0f0f0f0f);
17 i:=(i and $00ff00ff)+((i shr 8)and $00ff00ff);
18 i:=(i and $0000ffff)+((i shr 16)and $0000ffff);
19 exit(i);
20 end;
21 begin
22 assign(input,'cannon.in');reset(input);
23 assign(output,'cannon.out');rewrite(output);
24 readln(n,m);
25 for i:=1 to n do
26 begin
27 readln(s);
28 now[i]:=0;
29 for j:=1 to m do
30 if s[j]='H' then
31 inc(now[i],1 shl(m-j));
32 end;
33 fillchar(a,sizeof(a),0);
34 for i:=0 to 1 shl m-1 do
35 begin
36 if (i and(i shr 1)=0)and(i and(i shr 2)=0) then
37 begin
38 inc(a[0]);
39 a[a[0]]:=i;
40 b[a[0]]:=getsum(i);
41 end;
42 end;
43 fillchar(f,sizeof(f),0);
44 for i:=1 to a[0] do
45 f[1,i,1]:=b[i];
46 for i:=2 to n do
47 for j:=1 to a[0] do
48 for k:=1 to a[0] do
49 if (a[j] and now[i]=0)and(a[k]and now[i-1]=0)and(a[j] and a[k]=0) then
50 for l:=1 to a[0] do
51 if (a[l] and now[i-2]=0)and(a[l]and a[k]=0)and(a[l] and a[j]=0) then
52 f[i,j,k]:=max(f[i,j,k],f[i-1,k,l]+b[j]);
53 k:=0;
54 for i:=1 to a[0] do
55 for j:=1 to a[0] do
56 if f[n,i,j]>k then k:=f[n,i,j];
57 write(k);
58 close(input);close(output);
59 end.
posted @ 2011-11-03 22:22  N_C_Derek  阅读(416)  评论(0)    收藏  举报