【codevs2011】最小距离之和 [LNOI2013](Floyd)

题目网址:http://codevs.cn/problem/2011/

题目大意:有一个图,每次删一条边(可以重复删),求每次删边之后所有点对的最短距离之和。

  看了一眼题目,顿时发现了O(n^4)的暴力解法:每次删边之后跑一次Floyd,然后又看了一眼数据范围,发现n,m<=200……华丽丽的TLE……

  删边……好像不可做……但是如果是加边就好办了。看看能不能把删边转化成加边,于是……正解思路闪现了。

  其实这道题的思路就是把要删掉的边全部先删掉,跑一次Floyd,然后倒着加边,用加上的边更新最短路(设加上的边起点为x,终点为y,看i->x->y->j是否比直接i->j花费更小),就能在O(n^3)的时间内求出答案。

  代码:

var a:array[0..210,0..210]of longint;
  x,y,z,ans:array[0..210]of longint;
  n,m,i,j,k:longint;
  b:boolean;
begin
  read(n);
  for i:=1 to n do
    for j:=1 to n do
      read(a[i,j]);//读入图
  read(m);
  for i:=1 to m do begin
    read(x[i],y[i]); z[i]:=a[x[i],y[i]];
    a[x[i],y[i]]:=1<<29;//删边
  end;
  for k:=1 to n do
    for i:=1 to n do
      for j:=1 to n do
        if a[i,j]>a[i,k]+a[k,j] then a[i,j]:=a[i,k]+a[k,j];//跑Floyd
  for i:=m downto 1 do begin
    ans[i]:=0; b:=false;
    for j:=1 to n do begin
      for k:=1 to n do begin
        if a[j,k]>=1<<29 then begin
          ans[i]:=-1; b:=true; break;
        end;
        ans[i]:=ans[i]+a[j,k];
      end;
      if b then break;
    end;//算距离之和
    for j:=1 to n do
      for k:=1 to n do
        if a[j,k]>a[j,x[i]]+z[i]+a[y[i],k] then a[j,k]:=a[j,x[i]]+z[i]+a[y[i],k];//加边,更新答案
  end;
  for i:=1 to m do
    if ans[i]=-1 then writeln('INF')
    else writeln(ans[i]);//输出答案
end.
View Code

注:更新答案时不能用删掉的边直接覆盖掉原边,因为覆盖掉的花费可能更短。

原文地址:https://www.cnblogs.com/quzhizhou/p/6378621.html