利比亚行动

利比亚行动
(File IO): input:libyan.in output:libyan.out
时间限制: 1000 ms 空间限制: 262144 KB 具体限制
Goto ProblemSet

题目描述

2011年3月16日以来,利比亚爆发的骚乱不断升级,已严重危及到普通民众和各国在利比亚工作的人员的安全。为了尽快救出在利比亚的同胞,根据利比亚的形势,我国政府告诉每个在利比亚的公民,如何行动才能最快地到达安全的地方,然后由我国派出的飞机、轮船、汽车接回国。
假设将利比亚的地图划分为一个n行m列的长方形,待拯救的同胞小A在1行1列处,安全的目标位置在n行m列处。
利比亚是一个多沙漠的国家,经过某些位置需要消耗一定数量的食品,某些位置存储有很多很多食品。假定小A现在在位置i行j列,如果身上带有足够在新位置处需要消耗的食品的话,小A的下一个位置将到达i-1行j列、i+1行j列、i行j-1列、i行j+1列这四个位置之一,如果新位置处有食品的话,小A最多能拿一份该位置处的食品。当然如果小A已有的食品加上新位置处的一份食品数量超过小A能带食品的最高上限,小A就不能拿该位置处的食品了。
如果身上的食品不够到下一位置处的消耗,小A就不能到下一位置去,否则会面临生命危险的。当然,小A可以数次经过同一个位置,每次到此位置,都需要先消耗食品,然后可以拿一份食品(如果有的话),也就是小A可以多次到同一个地方来攒食品,也可以多次到同一个地方来消耗食品。若一个位置既要消耗食品,又可以拿食品,则小A必须先消耗食品,然后才能拿一份食品。
给出利比亚的地图,请告诉小A如何最快地从起点(1,1)走到终点(n,m)。程序只要输出最短路径长度就可以了。

输入

输入文件libyan.in的第一行有4个正整数n,m,t,maxc(1≤n≤200,1≤m≤200,0≤t≤maxc≤270),它们之间以一个空格分隔。表示利比亚的地形可以分为n行m列,小A一开始时的食品数量为t,身上最多能带maxc的食品。
接下来n行,每行m个字符,分别表示地图中该位置的信息。其中:
字符“”表示这个位置是建筑物、河流、有地雷等人无法走到的位置(保证起点终点不是“”);
数字字符“1”~“9”表示这个位置有该数量的食品;
小数点“.”表示人可以走到该位置,但该位置没有食品。
第n+2行只有一个正整数k,表示到达这k个位置会消耗食品。接下来k行,每行三个正整数x,y,w,表示每次到第x行y列处会消耗数量为w的食品。数据保证同一个位置不会出现两次。

输出

输出文件libyan.out只有一行,该行只有一个正整数。表示小A从起点到终点,在保证自身安全情况下走过的最短路径长度。

样例输入

【样例输入1】
3 5 0 0
.*…
…*.
...
0

【样例输入2】
4 5 2 5
..*.1
1.*..
*.1..
….1
4
1 2 2
3 2 1
4 2 3
4 3 3

样例输出

【样例输出1】
8

【样例输出2】
7

数据范围限制

70%的数据中,没有需要消耗食品的地方和存储有食品的地方。即在这些70%的数据中,输入的t=0,maxc=0,k=0,n行m列输入的地图只有小数点和*二种字符。
在上述70%的数据中,其中有40%的数据n,m均不超过100。

正解

第一眼看以为是DP或者什么高深的算法。
听老师一讲才发现是BFS+优化。

什么?

这么水?
怎么优化呢?当然是记忆化。
设f[i,j,k]为在第(j,k)位置上有i个食物的最小步数。这样一来就简单了。
(PS:题目描述很坑,是说在一个位置可以一下子全部拿走,但是下一次也可以全部拿走)

代码

var
        n,m,t,maxc,a1,b1,c1,i,j,k,num,xx,yy,xy:longint;
        bz:array[1..200,1..200] of longint;
        a:array[1..200,1..200] of char;
        ask:array[0..270,1..200,1..200] of longint;
        x,y,far,food:array[1..3000000] of longint;
        fx:array[1..4] of longint=(1,-1,0,0);
        fy:array[1..4] of longint=(0,0,1,-1);
        va:string;
begin
        assign(input,'libyan.in');
        assign(output,'libyan.out');
        reset(input);
        rewrite(output);
        readln(n,m,t,maxc);
        for i:=1 to n do
          begin
            for j:=1 to m do
              read(a[i,j]);
            readln;
          end;
        read(k);
        for i:=1 to k do
          begin
            read(a1,b1,c1);
            bz[a1,b1]:=c1;
          end;
        i:=1;
        j:=1;
        x[1]:=1;
        y[1]:=1;
        far[1]:=0;
        food[1]:=t;
        fillchar(ask,sizeof(ask),0);
        for i:=0 to 270 do
          for j:=1 to n do
            for k:=1 to m do
              ask[i,j,k]:=maxlongint;
            i:=1;
            j:=1;
        ask[0,1,1]:=0;
        while (i<=j) do
          begin
            for k:=1 to 4 do
              begin
                xx:=x[i]+fx[k];
                yy:=y[i]+fy[k];
                if not((xx>0) and (xx<=n) and (yy>0) and (yy<=m)) then
                  continue;
                if food[i]then
                  continue;
                val(a[xx,yy],num);
                xy:=food[i]-bz[xx,yy]+num;
                if xy>maxc then
                  xy:=maxc;
                if  (a[xx,yy]<>'*') and (ask[xy,xx,yy]>far[i]+1) then
                  begin
                    if food[i]>=bz[xx,yy] then
                      begin
                        inc(j);
                        ask[xy,xx,yy]:=far[i]+1;
                        food[j]:=xy;
                        far[j]:=far[i]+1;
                        x[j]:=xx;
                        y[j]:=yy;
                        if (x[j]=n) and (y[j]=m) then
                          begin
                            writeln(far[j]);
                            halt;
                          end;
                      end;
                  end;
              end;
            inc(i);
          end;

        close(input);
        close(output);
end.

你可能感兴趣的:(利比亚行动)