【图论】【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.

反思

知其然,知其所以然

posted @ 2011-02-27 20:13  liukee  阅读(441)  评论(1编辑  收藏  举报