【图论】【poj 3259】Wormholes
问题
http://poj.org/problem?id=3259
分析
既有虫洞又有普通道路,我们把虫洞看成是一条负权路,问题就转化成求一个图中是否存在负权回路,有两种算法:
1.bellman_ford算法
Bellman-Ford算法流程分为三个阶段:
(1)初始化:将除源点外的所有顶点的最短距离估计值 d[v] ←+∞, d[s] ←0;
(2)迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)
(3)检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 d[v]中。
2.spfa算法
我们都知道spfa算法是对bellman算法的优化,那么如何用spfa算法来判断负权回路呢?我们考虑一个节点入队的条件是什么,只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变。因此,用一个先进先出的队列来存放被成功松弛的顶点。同样,我们有这样的定理:“两点间如果有最短路,那么每个结点最多经过一次。也就是说,这条路不超过n-1条边。”(如果一个结点经过了两次,那么我们走了一个圈。如果这个圈的权为正,显然不划算;如果是负圈,那么最短路不存在;如果是零圈,去掉不影响最优值)。也就是说,每个点最多入队n-1次(这里比较难理解,需要仔细体会,n-1只是一种最坏情况,实际中,这样会很大程度上影响程序的效率)。
有了上面的基础,思路就很显然了,加开一个数组记录每个点入队的次数(turn),然后,判断当前入队的点的入队次数,如果大于n-1,则说明存在负权回路。
code1(bellman_ford)
type ed=record a,b,t:longint; end; var e:array[1..6000]of ed; dis:array[1..500]of longint; sum,i,j,jj,n,m,f,w:longint; fff:boolean; function bellman_ford:boolean; var i,j :integer; begin for i:=1 to n do for j:=1 to sum do with e[j] do if dis[a]+t<dis[b] then dis[b]:=dis[a]+t; for i:=1 to sum do with e[i] do if dis[a]+t<dis[b] then exit(false); exit(true) end; begin readln(f); for jj:=1 to f do begin sum:=0; fff:=false; readln(n,m,w); for i:=1 to m do begin inc(sum); readln(e[sum].a,e[sum].b,e[sum].t); inc(sum); e[sum].a:=e[sum-1].b; e[sum].b:=e[sum-1].a; e[sum].t:=e[sum-1].t; end; for i:=1 to w do begin inc(sum); readln(e[sum].a,e[sum].b,e[sum].t); e[sum].t:=0-e[sum].t; end; for i:=1 to n do dis[i]:=0; if not bellman_ford then writeln('YES') else writeln('NO'); end; end.
code2(spfa)
program liukee; var a:array[1..1000,0..1000] of longint; way:array[1..1000,0..1000] of longint; v:array[1..1000] of boolean; q:array[1..100000] of longint; d,turn:array[1..1000] of longint; zu,n,m,w,i:longint; procedure init; var i,x,y,z:longint; begin readln(n,m,w); for i:=1 to m do begin readln(x,y,z); inc(a[x,0]); inc(way[x,0]); a[x,a[x,0]]:=y; way[x,way[x,0]]:=z; inc(a[y,0]); inc(way[y,0]); a[y,a[y,0]]:=x; way[y,way[y,0]]:=z; end; for i:=1 to w do begin readln(x,y,z); inc(a[x,0]); inc(way[x,0]); a[x,a[x,0]]:=y; way[x,way[x,0]]:=-z; end; end; procedure spfa; var l,r,i,now:longint; begin l:=0; r:=1; for i:=1 to n do d[i]:=maxlongint; q[1]:=1; d[1]:=0; turn[1]:=1; while l<r do begin inc(l); now:=q[l]; v[now]:=false; for i:=1 to a[now,0] do if d[a[now,i]]>d[now]+way[now,i] then begin d[a[now,i]]:=d[now]+way[now,i]; if not(v[a[now,i]]) then begin inc(r); inc(turn[a[now,i]]); q[r]:=a[now,i]; v[q[r]]:=true; if turn[a[now,i]]>n then begin writeln('YES'); exit; end; end; end; end; writeln('NO'); end; begin readln(zu); for i:=1 to zu do begin fillchar(a,sizeof(a),0); fillchar(v,sizeof(v),false); fillchar(q,sizeof(q),0); fillchar(turn,sizeof(turn),0); fillchar(way,sizeof(way),0); fillchar(d,sizeof(d),0); init; spfa; end; end.
反思
知其然,知其所以然