【spfa+拆点】城市交通

 

城市交通

 

题目

某城市 有N(1<=N<=50)个街区,某些街区由公共汽车线路相连,如在图1中,街区1,2有一条公共汽车线路相连,且由街区1至街区2的时间为34分钟。由于街区与街区之间的距离较近,与等车时间相比可忽略不记,所以这个时间为两趟公共汽车的间隔时间,即平均的等车时间。


由街区1至街区5的最快走法为1-3-5,总时间为44分钟。

现在市政府为了提高城市交通质量,决定加开M(1<=M<=10)条公共汽车线路。若在某两个街区A,B之间加开线路(前提是A、B之间必须已有线路),则从A到B的旅行时间缩小为原来的一半(距离未变,只是等车的时间缩短了一半)。例如,若在1,2之间加开一条线路,则时间变为17分钟,加开两条线路,时间变为8.5分钟,以此类推。所有的线路都是环路,即如果由1至2的时间变为17分钟,则由2至1的时间也变为17分钟。

 求加开某些线路,能使由城市1至城市N的时间最少。例如,在图1中,如果M=2,则改变1-3,3-5的线路,总的时间可以减少为22分钟。

 

输入

 

    输入文件名为City.Inp。

第一行为城市数N与加开线路数M。

第二行至第N+1行,每行为N个实数,第I+1行第J列表示由城市I到城市J的时间。

如果时间为0,则城市I不可能到城市J。

注意:输入数据中,从城市1到城市N至少有一条路线。

 

输出

 

  输出文件名为City.Out.

   第一行为由城市1到城市N的最小时间X(保留小数点后两位)。

   第二行至第M+1行为更改的线路。每行由两个整数(x,y)构成。表示将城市x与城市y之间增加一条线路。

 

样例输入

 

City.Inp

5 2

0 34 24 0 0

34 0 10 12 0

24 10 0 16 20

0 12 16 0 30

0 0 20 30 0

 

样例输出

 

   City.Out

   22.00

   1 3

   3 5

=======================

spfa拆点

========================================

type
  node=record
         m:longint;
         x:longint;
       end;
var
  n,m:longint;
  map:array[1..50,1..50]of longint;
  dist:array[1..50,0..10]of extended;
  f_bo:array[1..50,0..10]of boolean;
  g:array[1..50,0..10]of node;
  dui:array[1..200000]of node;
procedure init;
begin
  assign(input,'city.in');
  assign(output,'city.out');
  reset(input); rewrite(output);
end;

procedure terminate;
begin
  close(input); close(output);
  halt;
end;

procedure print(x,y:longint);
var
  i:longint;
begin
  if y=m then terminate;
  if g[x,y].m=y then
    begin
      print(g[x,y].x,g[x,y].m)
    end
    else
    begin
      for i:=y to g[x,y].m-1 do
        writeln(g[x,y].x,' ',x);
      print(g[x,y].x,g[x,y].m);
    end;
end;

procedure spfa;
var
  i,j:longint;
  l,r:longint;
  now_x,now_m:longint;
  t:longint;
begin
  //fillchar(dist,sizeof(dist),$7);
  for i:=1 to n do
    for j:=0 to m do
      dist[i,j]:=99999999999;
  fillchar(f_bo,sizeof(f_bo),true);
  dist[1,m]:=0;
  f_bo[1,m]:=false;

  l:=0; r:=1;
  dui[r].m:=m;
  dui[r].x:=1;
  repeat
    inc(l);
    now_x:=dui[l].x;
    now_m:=dui[l].m;
    for i:=1 to n do
      if map[i,now_x]<>0 then
        begin
          t:=1;
          for j:=now_m downto 0 do
            begin
              if map[now_x,i]/t+dist[now_x,now_m]<dist[i,j] then
                begin
                  dist[i,j]:=map[now_x,i]/t+dist[now_x,now_m];
                  g[i,j].x:=now_x;
                  g[i,j].m:=now_m;
                  if f_bo[i,j] then
                    begin
                      f_bo[i,j]:=false;
                      inc(r);
                      dui[r].m:=j;
                      dui[r].x:=i;
                    end;
                end;
              t:=t shl 1;
            end;
        end;
    f_bo[now_x,now_m]:=true;
  until l>=r;
  writeln(dist[n,0]:0:2);
  print(n,0);
end;

procedure main;
var
  i,j:longint;
begin
  readln(n,m);
  for i:=1 to n do
    for j:=1 to n do
      read(map[i,j]);
  spfa;
end;

begin
  init;
  main;
  terminate;
end.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值