题目描述
植物大战僵尸 【问题描述】 Plants vs. Zombies(PVZ)是最近十分风靡的一款小游戏。Plants(植物)和Zombies(僵尸)是游戏的主角,其中Plants防守,而Zombies进攻。该款游戏包含多种不同的挑战系列,比如Protect Your Brain、Bowling等等。其中最为经典的,莫过于玩家通过控制Plants来防守Zombies的进攻,或者相反地由玩家通过控制Zombies对Plants发起进攻。 现在,我们将要考虑的问题是游戏中Zombies对Plants的进攻,请注意,本题中规则与实际游戏有所不同。游戏中有两种角色,Plants和Zombies,每个Plant有一个攻击位置集合,它可以对这些位置进行保护;而Zombie进攻植物的方式是走到植物所在的位置上并将其吃掉。 游戏的地图可以抽象为一个N行M列的矩阵,行从上到下用0到N–1编号,列从左到右用0到M–1编号;在地图的每个位置上都放有一个Plant,为简单起见,我们把位于第r行第c列的植物记为Pr, c。 Plants分很多种,有攻击类、防守类和经济类等等。为了简单的描述每个Plant,定义Score和Attack如下: Score[Pr, c] Zombie击溃植物Pr, c可获得的能源。若Score[Pr, c]为非负整数,则表示击溃植物Pr, c可获得能源Score[Pr, c],若为负数表示击溃Pr, c需要付出能源 -Score[Pr, c]。 Attack[Pr, c] 植物Pr, c能够对Zombie进行攻击的位置集合。 Zombies必须从地图的右侧进入,且只能沿着水平方向进行移动。Zombies攻击植物的唯一方式就是走到该植物所在的位置并将植物吃掉。因此Zombies的进攻总是从地图的右侧开始。也就是说,对于第r行的进攻,Zombies必须首先攻击Pr, M-1;若需要对Pr, c(0≤c<M-1)攻击,必须将Pr,M-1, Pr, M-2 … Pr, c+1先击溃,并移动到位置(r, c)才可进行攻击。 在本题的设定中,Plants的攻击力是无穷大的,一旦Zombie进入某个Plant的攻击位置,该Zombie会被瞬间消灭,而该Zombie没有时间进行任何攻击操作。因此,即便Zombie进入了一个Plant所在的位置,但该位置属于其他植物的攻击位置集合,则Zombie会被瞬间消灭而所在位置的植物则安然无恙(在我们的设定中,Plant的攻击位置不包含自身所在位置,否则你就不可能击溃它了)。 Zombies的目标是对Plants的阵地发起进攻并获得最大的能源收入。每一次,你可以选择一个可进攻的植物进行攻击。本题的目标为,制定一套Zombies的进攻方案,选择进攻哪些植物以及进攻的顺序,从而获得最大的能源收入。 【输入文件】 输入文件pvz.in的第一行包含两个整数N, M,分别表示地图的行数和列数。 接下来N×M行描述每个位置上植物的信息。第r×M + c + 1行按照如下格式给出植物Pr, c的信息:第一个整数为Score[Pr, c], 第二个整数为集合Attack[Pr, c]中的位置个数w,接下来w个位置信息(r’, c’),表示Pr, c可以攻击位置第r’ 行第c’ 列。 【输出文件】 输出文件pvz.out仅包含一个整数,表示可以获得的最大能源收入。注意,你也可以选择不进行任何攻击,这样能源收入为0。 【输入样例】 3 2 10 0 20 0 -10 0 -5 1 0 0 100 1 2 1 100 0 【输出样例】 25 【样例说明】 在样例中, 植物P1,1可以攻击位置(0,0), P2, 0可以攻击位置(2,1)。 一个方案为,首先进攻P1,1, P0,1,此时可以攻击P0,0 。共得到能源收益为(-5)+20+10 = 25。注意, 位置(2,1)被植物P2,0保护,所以无法攻击第2行中的任何植物。 【大致数据规模】 约20%的数据满足1 ≤ N, M ≤ 5; 约40%的数据满足1 ≤ N, M ≤ 10; 约100%的数据满足1 ≤ N ≤ 20,1 ≤ M ≤ 30,-10000 ≤ Score ≤ 10000
解法1:搜索
1 program pvz; 2 type 3 ty3=array[0..30,0..20] of longint; 4 ty1=^ty2; 5 ty2=record 6 x,y:longint; 7 next:ty1; 8 end; 9 var 10 n,m,i,j,k,l,x,y,ans:longint; 11 first:array[0..30,0..20] of ty1; 12 map,scr:ty3; 13 get:array[0..30,0..20] of boolean; 14 //=================== 15 procedure insert(x,y,i,j:longint); 16 var 17 p:ty1; 18 begin 19 new(p); 20 p^.x:=i; 21 p^.y:=j; 22 p^.next:=first[x,y]; 23 first[x,y]:=p; 24 end; 25 //=================== 26 procedure find(s:longint); 27 var 28 i,j:longint; 29 p:ty1; 30 begin 31 if ans<s then ans:=s; 32 for i:=1 to n do 33 for j:=1 to m do 34 if (map[i,j]=0)and(not get[i,j])and(get[i-1,j]) then 35 begin 36 get[i,j]:=true; 37 p:=first[i,j]; 38 while p<>nil do 39 begin 40 dec(map[p^.x,p^.y]); 41 p:=p^.next; 42 end; 43 find(s+scr[i,j]); 44 get[i,j]:=false; 45 p:=first[i,j]; 46 while p<>nil do 47 begin 48 inc(map[p^.x,p^.y]); 49 p:=p^.next; 50 end; 51 end; 52 end; 53 //=================== 54 begin 55 assign(input,'pvz.in'); reset(input); 56 assign(output,'pvz.out'); rewrite(output); 57 read(n,m); 58 for i:=1 to n do 59 for j:=1 to m do 60 begin 61 read(scr[m-j+1,i],k); 62 for l:=1 to k do 63 begin 64 read(x,y); 65 insert(m-j+1,i,m-y,x+1); 66 inc(map[m-y,x+1]); 67 end; 68 end; 69 k:=m; m:=n; n:=k; 70 for i:=1 to m do get[0,i]:=true; 71 find(0); 72 writeln(ans); 73 close(input); close(output); 74 end.
解法2:网络流
1 program pvz; 2 const max=10000000; 3 var 4 n,m,tot,s,t:longint; 5 g:array[0..1000,0..1000] of longint; 6 d,vd,rb,scr:array[0..1000] of longint; 7 //================== 8 function min(a,b:longint):longint; 9 begin 10 if a<b then exit(a) else exit(b); 11 end; 12 //================== 13 procedure built; 14 var 15 i,j,l,k,x,y,p:longint; 16 begin 17 read(n,m); 18 s:=0; t:=n*m+1; 19 for i:=1 to n do 20 for j:=1 to m do 21 begin 22 read(scr[(i-1)*m+j],k); 23 p:=scr[(i-1)*m+j]; 24 if p>=0 then 25 begin 26 g[s,(i-1)*m+j]:=p; 27 inc(tot,p); 28 end else g[(i-1)*m+j,t]:=-p; 29 for l:=1 to k do 30 begin 31 read(x,y); 32 if g[x*m+y+1,(i-1)*m+j]=0 then inc(rb[x*m+y+1]); 33 g[x*m+y+1,(i-1)*m+j]:=max; 34 end; 35 if j>1 then 36 begin 37 if g[(i-1)*m+j-1,(i-1)*m+j]=0 then inc(rb[(i-1)*m+j-1]); 38 g[(i-1)*m+j-1,(i-1)*m+j]:=max; 39 end; 40 end; 41 while true do 42 begin 43 p:=0; 44 for j:=1 to t-1 do 45 if rb[j]=0 then 46 begin p:=j; break; end; 47 if p=0 then break; 48 rb[p]:=max; 49 for k:=1 to t-1 do 50 if g[k,p]>0 then dec(rb[k]); 51 end; 52 for i:=1 to t-1 do 53 if rb[i]<10000 then 54 begin 55 g[s,i]:=0; g[i,t]:=0; 56 if scr[i]>0 then dec(tot,scr[i]); 57 end; 58 end; 59 //================== 60 function sap(x,flow:longint):longint; 61 var 62 i,tmp:longint; 63 begin 64 if x=t then exit(flow); 65 sap:=0; 66 for i:=0 to t do 67 if (g[x,i]>0)and(d[i]+1=d[x]) then 68 begin 69 tmp:=sap(i,min(flow-sap,g[x,i])); 70 dec(g[x,i],tmp); 71 inc(g[i,x],tmp); 72 inc(sap,tmp); 73 if sap=flow then exit; 74 end; 75 if d[s]=t+1 then exit; 76 dec(vd[d[x]]); 77 if vd[d[x]]=0 then d[s]:=t+1; 78 inc(d[x]); inc(vd[d[x]]); 79 end; 80 //================== 81 procedure main; 82 var 83 flow:longint; 84 begin 85 flow:=0; 86 vd[0]:=t+1; 87 while d[s]<t+1 do inc(flow,sap(s,maxlongint)); 88 writeln(tot-flow); 89 end; 90 //================== 91 begin 92 assign(input,'pvz.in'); reset(input); 93 assign(output,'pvz.out'); rewrite(output); 94 built; 95 main; 96 close(input); close(output); 97 end.