jzoj 1916. 【2011集训队出题】飞飞侠

Description

  飞飞国是一个传说中的国度,国家的居民叫做飞飞侠。飞飞国是一个N×M的矩形方阵,每个格子代表一个街区。
  然而飞飞国是没有交通工具的。飞飞侠完全靠地面的弹射装置来移动。
  每个街区都装有弹射装置。使用弹射装置是需要支付一定费用的。而且每个弹射装置都有自己的弹射能力。
  我们设第i行第j列的弹射装置有Aij的费用和Bij的弹射能力。并规定有相邻边的格子间距离是1。那么,任何飞飞侠都只需要在(i,j)支付Aij的费用就可以任意选择弹到距离不超过Bij的位置了。如下图
  
这里写图片描述
  (从红色街区交费以后可以跳到周围的任意蓝色街区。)
  现在的问题很简单。有三个飞飞侠,分别叫做X,Y,Z。现在它们决定聚在一起玩,于是想往其中一人的位置集合。告诉你3个飞飞侠的坐标,求往哪里集合大家需要花的费用总和最低。

Input

  输入的第一行包含两个整数N和M,分别表示行数和列数。
  接下来是2个N×M的自然数矩阵,为Bij和Aij
  最后一行六个数,分别代表X,Y,Z所在地的行号和列号。

Output

  第一行输出一个字符X、Y或者Z。表示最优集合地点。
  第二行输出一个整数,表示最小费用。
  如果无法集合,只输出一行NO

Sample Input

4 4
0 0 0 0
1 2 2 0
0 2 2 1
0 0 0 0
5 5 5 5
5 5 5 5
5 5 5 5
5 5 5 5
2 1 3 4 2 2

Sample Output

Z
15

Data Constraint

Hint

【数据范围】
  20% N, M ≤ 10; Bij ≤ 20
  40% N, M ≤ 100; Bij ≤ 20
  100% 1 ≤ N, M ≤ 150; 0 ≤ Bij ≤ 10^9; 0 ≤ Aij ≤ 1000

分析:暴力spfa,连边会爆,显然可以带着坐标跑……

代码:

const
 maxn=150;
 maxv=maxn*maxn;
 ymw=100000000000000;

type
 node=record
  x,y:longint;
 end;

var
 a,b:array [1..maxn,1..maxn] of int64;
 n,m,i,h,t,j:longint;
 ans:int64;
 x,y:array [1..3] of longint;
 dis:array [0..maxn,0..maxn] of int64;
 v:array [1..maxn,1..maxn] of boolean;
 list:array [1..maxv] of node;
 s:array [1..3,1..3] of int64;
 ch:char;

procedure init;
 var i,j:longint;
begin
 readln(n,m);
 for i:=1 to n do
  for j:=1 to m do
   read(a[i,j]);
 for i:=1 to n do
  for j:=1 to m do
   read(b[i,j]);
 for i:=1 to 3 do
  read(x[i],y[i]);
end;

function max(x,y:longint):longint;
 begin
  if x>y then exit(x)
         else exit(y);
 end;

function min(x,y:longint):longint;
 begin
  if x<y then exit(x)
         else exit(y);
 end;

procedure spfa(xx,yy:longint);
 var x,y,i,j:longint;
begin
 for i:=1 to n do
  for j:=1 to m do
   begin
    dis[i,j]:=ymw;
    v[i,j]:=false;
    list[(i-1)*m+j].x:=0;
    list[(i-1)*m+j].y:=0;
   end;
 h:=0; t:=1;
 list[t].x:=xx; list[t].y:=yy;
 dis[xx,yy]:=0;
 v[xx,yy]:=true;
 repeat
  h:=h mod maxv+1;
  x:=list[h].x;
  y:=list[h].y;
  for i:=max(1,x-a[x,y]) to min(n,x+a[x,y]) do
   for j:=max(1,y-a[x,y]+abs(x-i)) to min(m,y+a[x,y]-abs(x-i)) do
     if (dis[i,j]>dis[x,y]+b[x,y]) then
      begin
        dis[i,j]:=dis[x,y]+b[x,y];
        if v[i,j]=false then
         begin
            t:=t mod maxv+1;
            list[t].x:=i;
            list[t].y:=j;
            v[i,j]:=true;
         end;
    end;
  v[x,y]:=false;
 until h=t;
end;

begin
 init;
 for i:=1 to 3 do
  begin
   spfa(x[i],y[i]);
   for j:=1 to 3 do
    s[i,j]:=dis[x[j],y[j]];
  end;
 ans:=maxlongint;
 if (s[2,1]<>ymw) and (s[3,1]<>ymw) then
  begin
   ch:='X';
   ans:=s[2,1]+s[3,1];
  end;
 if (s[1,2]<>ymw) and (s[3,2]<>ymw)
  and (s[1,2]+s[3,2]<ans) then
  begin
   ch:='Y';
   ans:=s[1,2]+s[3,2];
  end;
 if (s[1,3]<>ymw) and (s[2,3]<>ymw)
  and (s[1,3]+s[2,3]<ans) then
  begin
   ch:='Z';
   ans:=s[1,3]+s[2,3];
  end;
 if ans=maxlongint then begin writeln('NO'); exit; end
  else
   begin
    writeln(ch);
    writeln(ans);
   end;
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值