790 likes | 1.07k Views
图算法(二). 单源最短路经 Single-Source Shortest Path. 单源最短路径. 问题 : 带权有向图 G, 找出从给定 源顶点 s 到其它顶点 v 的权最小路径。 “ 最短路径 ” = 最小权 路径的权是路径上所有边的权之和。 例:道路图 : 从福州大学到东街口的最短路径是什么 ?. 权非负的单源最短路径算法 ( Dijkstra ). 贪心算法 基本思想 : 设置一个顶点集 S ,不断做贪心选择扩充这个集合。 一个顶点属于 S 当且仅当从源到该顶点最短路径长度已知。. 权非负的单源最短路径算法 ( Dijkstra ).
E N D
图算法(二) 单源最短路经 Single-Source Shortest Path
单源最短路径 • 问题: 带权有向图G, 找出从给定源顶点s到其它顶点v的权最小路径。 • “最短路径”= 最小权 • 路径的权是路径上所有边的权之和。 • 例:道路图 : 从福州大学到东街口的最短路径是什么?
权非负的单源最短路径算法(Dijkstra) • 贪心算法 • 基本思想: 设置一个顶点集S,不断做贪心选择扩充这个集合。 一个顶点属于S当且仅当从源到该顶点最短路径长度已知。
权非负的单源最短路径算法(Dijkstra) • 初始时,S仅包含源s, • 算法每次从V-S中取出具有最短特殊路径 长度的顶点u加入S中。 • 特殊路径: 从源到G中某一顶点u且中间只经过S中顶点的路称为从源到u的特殊路径。
权非负的单源最短路径算法(Dijkstra) Const P133 maxvalue=99999.0 maxlength=100; Type Arr1=array[1..maxlength] of integer; Arr2=array[1..maxlength,1..maxlength] of real; Arr3=array[1..maxlength] of real; Var prev:Arr1; c:Arr2; dist:Arr3; s:array[1..maxlength] of boolean n:integer; {邻接矩阵,c[i,j]为权} {dist[i]当前从源到顶点i的最短特殊路径长度(仅经过S中点) } {到该顶点最短路径长度 已知的顶点集S }
B 2 10 A 4 3 D 5 1 C Ex: run the algorithm 权非负的单源最短路径算法(Dijkstr) Procedure shortpaths(n,v:integer); {单源最短路径问题的Digkstra算法} Var i,j,u:integer; temp,newdist:real; begin for i:=1 to n do begin dist[i]:=c[v,i]; s[i]:=false; if(dist[i]=maxvalue) then prev[i]:=0 else prev[i]:=v; end; Dist[v]:=0;s[v]:=true; {v到i的当前最短路径长度} 初始化 {源v加入到S}
从未加入S中的顶点 中选取当前特殊距 离最短的顶点加入 S 权非负的单源最短路径算法(Dijkstra) For i:=1 to n-1 do begin temp:=maxvalue; u:=v; for j:=1 to n do if ((not s[j]) and (dist[j]<temp)) then begin u:=j; temp:=dist[j]; end; s[u]:=true; Temp变量中保存的是什么值?
放松步 权非负的单源最短路径算法(Dijkstra) For j:=1 to n do if ((not s[j]) and (c[u,j]<maxvalue))then begin newdist:=dist[u]+c[u,j]; if (newdist<dist[j]) then begin dist[j]:=newdist; prev[j]:=u; end end end end; u是新加入S的顶点, 计算u的所有相邻顶点的特殊距离。若比原距离小,则用新距离代替,并让u做为最短路径上的点
2 2 10 1 4 3 4 5 1 3 Ex: run the algorithm If Dist[u]+c[u,j]<dist[j] then dist[j]=dist[u]+c[u,j] prev[j]=u S u dist[2] dist[3] dist[4] 1 - 10 5 maxvalue 1,3 3 9 5 6 1,3,4 4 8 5 6 1,3,4,2 2 8 5 6
权非负的单源最短路径算法(Dijkstra) • 基于邻接表的算法(当图边数远小于|V|2时采用)P136 Const maxint=2147483647 maxlength=1000 Type pointer=^adjnode;//邻接表 adjnode=record v:integer; //顶点标号 w:integer; //权 next:pointer; //指向下一邻接点指针 end; Arr1=array[1..maxlength] of longint; Arr2=array[1..maxlength] of integer; Arr3=array[1..maxlength] of pointer; Var dist: Arr1; //特殊距离 prev:Arr2; //i的前一节点 adj:Arr3; //邻接表 from,tto,n,e:integer; //源,目标,节点数,边数
负距离最大, 正距离则最小 权非负的单源最短路径算法(Dijkstra) Function DeleteMin:integer; //取当前负距离最大(正距离最小)的顶点 Var i,k:integer; temp:longint; begin k:=0; temp:=-maxint; for i:=1 to n do if (dist[i]<0) and (dist[i]>temp) then begin temp:=dist[i]; k:=i; end; DeleteMin:=k; End;
初始化当前距离为某一较小负整数,dist[i]<0表示i∈V-S初始化当前距离为某一较小负整数,dist[i]<0表示i∈V-S dist[i]>0表示i∈S, 省略数组S 节点i加入S后,dist[i]=- dist[i],变为正距离。 权非负的单源最短路径算法(Dijkstra) Function shortpath(from,tto:integer):boolean; Var i,k:integer; p:pointer; empty:boolean; begin for i:=1 to n do begin dist[i]:=-maxint; prev[i]:=0; end; k:=from; //源点 dist[k]:=0; //源点距离 empty:=false;
权非负的单源最短路径算法(Dijkstra) while (not empty) do {还有顶点未被加入S或未到目标顶点} Begin p:=adj[k]; while (p<>nil) do begin if (dist[p^.v]<0) and (dist[p^,v]<-(dsit[k]+P^.w)) then begin dist[p^.v]:=-(dist[k]+p^.w); prev[p^.v]:=k; end; p:=p^.next; End; K的第一个邻接点P 逐一考察K的所有邻接点,执行放松步 未加入S 放松步 K的下一个邻接点
权非负的单源最短路径算法(Dijkstra) k:=deletemin; //取当前负距离最大(正距离最小)点作为S中的点, If (k=0) or (k=tto) then empty:=true; //找不到当前特殊路径距离更小的顶点或到到目标节点 dist[k]:=-dist[k]; //k加入S End; Shortpath:=(k=tto); End;
2 2 10 1 4 3 4 5 1 3 Ex: run the algorithm 权非负的单源最短路径算法(Dijkstra) If Dist[u]+c[u,i]<dist[i] then dist[i]=dist[u]+c[u,i] prev[i]=u If –(Dist[u]+c[u,i])>dist[i] then dist[i]=-(dist[u]+c[u,i]) prev[i]=u k dist[2] dist[3] dist[4] DeleteMin 1 -10 -5 -maxint 3 -9 5 -6 4 -8 5 6 放松步 2 8 5 6 empty:=true;
有向无环图最短路径Directed acyclic graphs( DAG) Shortest Paths • 问题: 找出 DAG中的最短路径 • 思想: 利用拓扑排序(p138)。 若可构造DAG的顶点的拓扑排序,那么按该顺序可用O(|E|+|V|)时间计算最短路径。 排序后,第i+1个顶点最短路径上的点 一定是由前i个顶点中的点组成。(入度为0)
图:邻接表 1 3 nil 1 2 2 4 nil 2 4 2 3 3 4 3 nil 不能进行拓扑排序 存在环
拓扑排序算法 Function topsort:boolean; P140 var I,count:integer; indeg:Arr1; p:wpointer; q:queue; begin topsort:=true; count:=0; makenull(q); //清空队列q fillchar(indeg,sizeof(indeg),0);//数组indeg存储每个顶点入度
//通过遍历邻接表,计算所有顶点的入度 For i:=1 to n do begin p:=wadj[i]; //顶点i的邻接表的第一个邻接点 while p<>nil do //依次为顶点i的所有邻接点入度加1 begin inc(indeg[p^.v]); p:=p^.next; end; End;
//入度为0的顶点加入队列q For i:=1 to n do if indeg[i]=0 then enqueue(i,q); //入队 //队列q的队首顶点出队,与该顶点邻接的顶点的出度减1 While not empty(q) do begin ….P141 //入度为0的顶点数小于n时,存在环 If count<n then begin writeln(‘graph is cyclic’); topsort:=false; end; End;
有向无环图单源最短路径Directed acyclic graphs( DAG) Shortest Paths • Dag的单源最短路径问题具有最优子结构性质。 • 可对Dijkstra算法做进一步改进,可以在O(|E|+|V|)时间内求解单源最短路。
有向无环图单源最短路径Directed acyclic graphs( DAG) Shortest Paths Procedure reverse; P141 Var i:integer; Begin fillchar(top,sizeof(top),0); for i:=1 to n do top[ord[i]]:=i; End; 例:顶点 1 2 3 4 5 ord 2 3 1 5 4 top[ord[2]]=2 top 1 2 3 4 5top[2]=1 top[1]=3
不能排序?为什么不能??? 存在环 可以排序,计算DAG最短路径 有向无环图最短路径Directed acyclic graphs( DAG) Shortest Paths Function shortpath(s:integer):boolean; p142 var I,j,k:integer; p:wpointer; Begin if not topsort then begin shortpath:=false; exit; end; Reverse; Shortpath:=true; For i:=1 to n do begin dist[i]:=maxint; prev[i]:=0; end; dist[s]:=0;
按Top顺序考察所有排在顶点s之后的顶点 取与k相邻的所有顶点中当前特殊路径距离最短的点 放松步 有向无环图( DAG)最短路径Directed acyclic graphs Shortest Paths For i:=ord[s] to n do begin k:=top[i];{排在第i位顶点的顶点标号为k} p:=wadj[k]; {k的邻接点} while p<>nil do begin j:=p^.v; if dist[j]>dist[k]+p^.w then begin dist[j]:=dist[k]+p^.w; prev[j]:=k; end; p:=p^.next; end; end; End;
有负权边的有向图最短路径(Ford算法) Function shortpath(s:integer):boolean; P144 Var I,j,k:integer; p:wpointer; {图邻接表} Begin shortpath:=true; for i:=1 to n do begin dist[i]:=maxint; Count[i]:=0; prev[i]:=0; ub[i]:=false; {顶点i在队列qu中,则ub[i]=true} End; Makenull(qu);{清空队列qu} 变量初始化
有负权边的有向图最短路径(Ford算法) Dist[s]:=0 {源} Enqueue(s,qu); {源s入队} {当顶点的当前特殊路径长度改变时,该顶点入队qu. 对qu中顶点的邻接点执行放松步。} While (not empty(qu)) do begin k:=dequeue(qu); {k出队} ub[k]:=false; inc(count[k]); if count[k]>n then {存在负权环,无解} begin writeln(‘cycle of negative weight exists!’) shortpath:=false; exit; end; What will be the running time? A: O(VE)
有负权边的有向图最短路径(Ford算法) 逐一考察k的所有邻接点p,计算邻接点p经过顶点k的路径距离,若小于p原距离,则修改距离,p入队。 P:=wadj[k]; While (p<>nil)do begin j:=p^.v; if dist[j]:>dist[k]+p^.w then begin dist[j]:= dist[k]+p^.w ; prev[j]:=k; if not ub[j] then begin enqueue(j,qu); ub[j]:=true; end; end; P:=p^.next; end; end; end; 放松步 若j不在队列qu中,则入队
Ford Algorithm 例 qu dist[2] dist[3] dist[4] dist[5] 1 Maxint maxint maxint maxint 2,3 -1 4 maxint maxint 3,4,5 -1 2 1 1 4 -1 2 -2 1 - -1 2 -2 1 2 1 0 3 4 nil 2 -1 -1 2 s 1 5 2 3 1 -3 4 3 4 5 Ex: work on board
所有顶点对间的最短路径问题(Floyd算法) 每次以一个顶点为源,重复执行Dijkstra n次? A: O(|V|3) Floyd算法:设v={1,2,…,n},邻接矩阵a a[i,j]= 0 i=j = c(i,j) (i,j)E = ∞ i≠j.(i,j)E 对邻接矩阵a做n次迭代。经过k次迭代后,a[i.j]的值是顶点i到j且不经过编号大于k的顶点的最短路径长度。 a(k)[i,j]=min(a(k-1)[i,j], a(k-1)[i,k]+a(k-1)[k,j]) 算法实现P146
最短路径算法应用举例(一) 交通灯 • 问题描述 • D城道路连接不同路口,任意2个路口间至多有一条道路; • 任一道路不可连接同一路口; • 同一道路正反两方向通行时间相同; • 每一路口有一交通灯(蓝色和紫色交替变化),蓝色和紫 色信号都有特定持续时间 • 同一道路的两个路口的信号颜色相同时才可通行; • 当车辆到某路口时,该路口交通灯正在切换颜色,则当前 所显示的信号颜色是新切换的颜色 • 车辆不能通行时,允许其在路口等待。
最短路径算法应用举例(一) 交通灯 • 主要参数 • 2≤n≤300, n路口总数,1、2、…、n分别表示n个路口; • 1≤m≤14000,m是D城的道路数; • 1≤lij≤100,其中lij是从路口i到路口j的通行时间; • 1≤tic≤100,其中tic是路口i的交通灯显示颜色c的持续时间。C=B表示蓝色 C=P表示紫色; • 1≤ric≤tic,其中ric是路口i的交通灯初始颜色c的持续时间;
最短路径算法应用举例(一) 交通灯 • 输入数据 1 4 4 5 B 2 16 99 P 6 32 13 P 2 87 4 P 38 96 49 1 2 4 1 3 40 2 3 75 2 4 76 3 4 77 出发路口,到达路口
最短路径算法应用举例(一) 交通灯 • 输入数据 1 4 4 5 B 2 16 99 P 6 32 13 P 2 87 4 P 38 96 49 1 2 4 1 3 40 2 3 75 2 4 76 3 4 77 路口数n,道路数m
最短路径算法应用举例(一) 交通灯 • 输入数据 1 4 4 5 B 2 16 99 P 6 32 13 P 2 87 4 P 38 96 49 1 2 4 1 3 40 2 3 75 2 4 76 3 4 77 各路口的 初始颜色c,初始颜色持续时间ric ,蓝色持续时间tiB,紫色持续时间tiP
最短路径算法应用举例(一) 交通灯 • 输入数据 1 4 4 5 B 2 16 99 P 6 32 13 P 2 87 4 P 38 96 49 1 2 4 1 3 40 2 3 75 2 4 76 3 4 77 2 4 76 1 4 75 4 77 3 各道路的 道路连接的路口i,路口j,通行时间lij
最短路径算法应用举例(一) 交通灯 • 输出数据 127 1 2 4 2 4 75 1 4 最短通行时间 76 4 77 3 最短通行路径
最短路径算法应用举例(一) 交通灯 • 解题思路 • 带权无向图 • 利用交通灯控制,增加了路口等待时间。 权=通行时间+路口等待时间 2 4 75 可以用Dijkstra算法吗? 1 4 76 4 77 3 ???
最短路径算法应用举例(一) 交通灯 • 路口等待时间 权=通行时间+路口等待时间 (1)路口i与j颜色相同, 不必等待(wait=0)。 (2)颜色不同,等到颜色相同(最少等待时间)。 若i,j中有一个切换颜色(只有两种颜色)就相同,即i和j中最早切换颜色的时间是最少等待时间; 若i,j同时切换颜色,计算下一次最早切换时间; 若I,j连续三次同时切换颜色,则它们周期相同,i和j间道路永远不能通行。 2 4 75 1 4 76 4 77 3
最短路径算法应用举例(一) 交通灯 const maxint=2147483647; maxlength=300; Type pointer=^adjnode; adjnode=record v:integer; w:integer; next:pointer; end; lightype=record initcolor:integer; firstp:integer; initp:integer; secondp:integer; blue:integer; purple:integer; end; 图:邻接表 交通灯
最短路径算法应用举例(一) 交通灯 Arr1=array[1..maxlength] of longint; Arr2=array[1..maxlength] of integer; Arr3=array[1..maxlength] of pointer; Arr4=array[1..maxlength] of lighttype; Var dist:Arr1; prev:Arr2; adj:Arr3; light:Arr4; from,tto,n,e:integer; f:text; 当前特殊距离 最短路径的前一个顶点 顶点
最短路径算法应用举例(一) 交通灯 Function detcolor(i:integer;t:longint):integer; Begin detcolor:=light[i].initcolor;{初始颜色} if (t>=light[i].initp) and (((t-light[i].initp)mod(light[i].blue+light[i].purple))<light[i].secondp) then detcolor:=((light[i].initcolor+1)mod 2); End; 计算路口i在t时刻的交通灯颜色 初始颜色持续时间
最短路径算法应用举例(一) 交通灯 Function dettime(i:integer;color:integer;t:longint;var t1,t2,t3:integer); Begin if(t<light[i].initp)then begin t1:=light[i].initp-t; t2:=t1+light[i].secondp; t3:=t2+light[i].firstp; end else if (color=light[i].initcolor) then begin t1:=(light[i].secondp+light[i].firstp)-(t-light[i].initp)mod(light[i].secondp+light[i].firstp); t2:=t1+light[i].secondp; t3:=t2+light[i].firstp; end 计算路口it时刻后连续三次切换颜色的时间 时刻t小于初始颜色持续时间 时刻t大于初始颜色 持续时间,且颜色为初始颜色
最短路径算法应用举例(一) 交通灯 … else begin t1:=light[i].secondp-(t-light[i].initp)mod(light[i].secondp+light[i].firestp); t2:=t1+light[i].firstp; t3:=t2+light[i].secondp; end End; 时刻t大于初始颜色 持续时间,且颜色不是初始颜色