代码改变世界

SPFA算法

2010-09-24 20:14  snowkylin  阅读(451)  评论(0编辑  收藏  举报
program spfa;
const max=10000;
type nodep=^node;
     node=record data,wei:longint; next:nodep; end;
var g:array[1..max]of nodep;
    n,i,st:longint;
    p:nodep;
    dist:array[1..max]of longint;

procedure init;
    var i,e,x,y,z:longint;
        p:nodep;
    begin
        readln(n,e);
        fillchar(g,sizeof(g),0);
        for i:=1 to e do
            begin
                readln(x,y,z);
                if g[x]=nil then
                   begin new(g[x]); g[x]^.data:=y; g[x]^.wei:=z; g[x]^.next:=nil; end
                else begin new(p); p^.data:=y; p^.wei:=z; p^.next:=g[x]; g[x]:=p; end;
                if g[y]=nil then
                   begin new(g[y]); g[y]^.data:=x; g[y]^.wei:=z; g[y]^.next:=nil; end
                else begin new(p); p^.data:=x; p^.wei:=z; p^.next:=g[y]; g[y]:=p; end;
            end;
        readln(st);
    end;
procedure solve;
    var i,front,rear,k,u:longint;
        q:array[0..max-1]of longint;
        inq:array[1..max]of boolean;
    begin
        for i:=1 to n do dist[i]:=maxlongint;
        dist[st]:=0; front:=0; rear:=1;
        q[1]:=st;
        fillchar(inq,sizeof(inq),false);
        inq[st]:=true;
        while front<>rear do
            begin
                front:=(front+1)mod max;
                inq[st]:=false;
                k:=q[front];
                p:=g[k];
                while (p<>nil) do
                    begin
                        u:=p^.data;
                        if (dist[k]+p^.wei<dist[u]) then
                           begin
                               dist[u]:=dist[k]+p^.wei;
                               if not inq[u] then
                                  begin
                                      rear:=(rear+1)mod max;
                                      q[rear]:=u;
                                  end;
                           end;
                        p:=p^.next;
                    end;
            end;
    end;
begin
    assign(input,'spfa.in'); reset(input);
    assign(output,'spfa.out'); rewrite(output);
    init;
    solve;
    for i:=1 to n do write(dist[i],' ');
    close(input); close(output);
end.

输入文件:

7 8
1 2 6
1 4 5
1 6 8
2 3 3
3 5 4
4 3 2
4 6 4
5 7 1
1

 

松弛:

if (dist[k]+p^.w<dist[u]) then
dist[u]:=dist[k]+p^.w;

 

就是如果 从原点到点u(泛指所有与k相连的点)的距离+u到k的距离 小于 原来“预测”的点k到原点距离,就更新。形状如把直线拉成折线,故称松弛。

只要u点被松弛了,就可能从u点经过再得到新的最短路径,所以加入队列。

program spfa;
const max=10000;
type nodep=^node;
     node=record
                 data:longint;{from the n of a[n] to "data"}
                 w:longint;{the distance between point"n" to point"data"}
                 next:nodep;
                 end;
var g:array[1..max]of nodep;
    dist:array[1..max]of longint;
    i,n,e,st{the start point}:longint;

procedure init;
var i,x,y,z:longint;
    p:nodep;
begin
     readln(n,e);
     fillchar(g,sizeof(g),0);
     for i:=1 to e do
     begin
          readln(x,y,z);
          if g[x]=nil then
             begin
                  new(g[x]);
                  g[x]^.data:=y;
                  g[x]^.w:=z;
                  g[x]^.next:=nil;
             end
             else
             begin
                  new(p);
                  p^.data:=y;
                  p^.w:=z;
                  p^.next:=nil;
                  p^.next:=g[x];
                  g[x]:=p;
             end;
          if g[y]=nil then
             begin
                  new(g[y]);
                  g[y]^.data:=x;
                  g[y]^.w:=z;
                  g[y]^.next:=nil;
             end
             else
             begin
                  new(p);
                  p^.data:=x;
                  p^.w:=z;
                  p^.next:=nil;
                  p^.next:=g[y];
                  g[y]:=p;
             end;
     end;
     readln(st);
end;
procedure solve;
var inq:array[1..max]of boolean;{find if n is in queue"q"}
    q:array[1..max]of longint;
    i,front,rear,k,u:longint;
    p:nodep;
begin
     for i:=1 to n do dist[i]:=maxlongint;
     dist[st]:=0; front:=0; rear:=1;
     q[1]:=st;
     fillchar(inq,sizeof(inq),false);
     inq[st]:=true;
     while front<>rear do
     begin
          front:=(front+1)mod max;
          inq[st]:=false;
          k:=q[front];{pick out the front of "q"queue in k}
          p:=g[k];{the point linked to k,next use all of the point in p to do the "relaxation"}
          while (p<>nil) do
          begin
               u:=p^.data;{pick out the point linked to k}
               if (dist[k]+p^.w<dist[u]) then
               begin
                    dist[u]:=dist[k]+p^.w;
                    if not inq[u] then
                    begin
                         rear:=(rear+1)mod max;
                         q[rear]:=u;
                    end;
               end;
               p:=p^.next;{relax next point}
          end;
     end;
end;
begin
     while not eof do
     begin
          init;
          solve;
          for i:=1 to n do writeln(dist[i]);
     end;
end.