【图论】【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.
反思
知其然,知其所以然
浙公网安备 33010602011771号