单源最短路径
重点课题!
单源最短路径(SSSP)是这样一个问题:在一张图中,有一个源点S,点与点之间有边,边上有权值,表示从边的这头走到边的那头要付出的代价(距离)。求点S到其余所有点的最小距离。
书上讲了Dijkstra。朴素Dijkstra呢,NOIP一般就是小数据用用,O(n2)敷衍了事(如果你觉得用着顺的话);对付大数据也不是不行,但是得加个堆(相关章节自行查阅),用于维护和查询当前的距离最小的点再加上邻接表或边集数组,复杂度降为O(nlgn+e),不过编程复杂度较高,不赞成使用。但是Dijkstra的思路还是要搞懂的——就是说我每次都选“还没确定的”到源点距离最小的点i,他当前的到S的距离d[i]一定可以确定是i到S的最短距离:没有比他更小的没更新过他的点了。把这个点选出来(可以用堆)后,用他更新与他直接相连的若干点,跟他们说:哥们,走我这试试看,能不能更近点。然后西去修成正果——i也变成了“已确定的点”。做n-1个点后(d[s]=0),算法结束。
然而我要说的是——我们可以用宽搜做!状态就是当前点的编号!加上上面的数组d,这个叫SPFA的算法效果非常好!注意在Dijkstra中有个Relax操作:设i->j的边权为c[i,j],则我们可以做如下操作:if d[j]>d[i]+c[i,j] then d[j]=d[i]+c[i,j];。这步操作很关键。如果上述语句成功执行,那么无疑d[j]将变小,那么所有j连出去的点的d值也有可能变小,应此我们应当挨个访问一下。不急,我们学学人家宽搜,用个队列,把j扔队尾去(如果j不在队列的话)。
流程如下:
procedure SPFA(s:integer);
var
n,v,u,t1,t2:integer;
d:array[1..1000]of integer;
c:array[1..1000,1..1000]of integer;
queue:array[1..1000]of intger;
begin
t1=1;t2=1;q[1]=s;
repeat
u=q[t1];inc(t1);flag[u]=false;
for v:=1 to n do
if (c[u,v]>0) and (d[v]>d[u]+c[u,v]) then
begin
d[v]:=d[u]+c[u,v];
if not flag[v] then
begin
inc(t2);q[t2]:=v;flag[v]:=true;
end;
end;
until t1=t2;
end;
事实上,SPFA是NOIP中最常用的单源最短路径算法——也就是宽搜+判重(d数组和flag数组)。
对SPFA更深入的了解见姜碧野童鞋的2009国家队论文之《SPFA算法的优化及应用》。国家队论文是个好东西!!