题解 P1396 【营救】

曦行夜落

2018-03-16 21:15:33

Solution

由于我刚刚了解邻接链表(不信自己看学术版),所以这里发一发题解,顺便重新理一遍 SPFA都应该了解,下面挂出框架 ``` while 队列不为空 { 取队首,队首出队 枚举每一条边,如果起点为队首 对这条边进行一次松弛 (也就是三角形不等式dist[s]=min(dist[t]+map[s][t],dist[s])) 如果成功,入队 } ``` 上面的是没有任何优化的朴素SPFA,显然,这种算法将有可能退化为BellmanFord 这时,我们使用一个全新的图存储方式~~,其实也不新~~ 可以发现,这个算法最危险的地方就是枚举每一条边,如果能够直接知道要松弛哪些边,该多好啊! 这时就轮到邻接链表出场了。接下来介绍几个重要数组。 head[i]:起点是i的第一条边(其实大部分时候是最后一条,但是一般从这个head[i]开始搜索) next[i]:表示第i条边同一起点的下一条边 to[i]:表示第i条边的终点 w[i]:表示第i条边的边权 研究完这些,离成功就不远了。首先看最重要的addedge函数 (注意是P,看得懂吧?) ```pascal procedure addedge(u,v,w:longint); begin inc(tot); //边数增加 e[tot].too:=v; //设置终点为v e[tot].l:=w; //边权 e[tot].nxt:=first[u]; //注意!将老的第一条边设置为新的的后继 first[u]:=tot; //新的第一条边 end; ``` 以上就是这个算法的精华,设队首为u,接下来—— 我们将SPFA中的枚举每一条边改为从head[u]开始,每次松弛,然后将u替换为next[u],就可以啦!具体看代码吧 ```pascal var e:array[0..500050] of record //结构体e,存to,w和next too,l,nxt:longint; end; first,dist,q:array[0..500050] of longint; //head、dist、队列 flag:array[0..500050] of boolean; //队列标记 n,m,i,j,head,tail,s,t,f,g,w,p,tot:longint; //各种变量 function max(x,y:longint):longint; //max,用完cpp偶尔一次P觉得好麻烦 begin if (x>y) then exit(x); exit(y); end; procedure addedge(u,v,w:longint); //边的构造 begin inc(tot); e[tot].too:=v; e[tot].l:=w; e[tot].nxt:=first[u]; first[u]:=tot; end; begin readln(n,m,s,t); for i:=1 to m do first[i]:=-1; //没有下一条边 tot:=0; for i:=1 to m do begin readln(f,g,w); addedge(f,g,w); addedge(g,f,w); //双向! end; for i:=1 to n do dist[i]:=maxlongint; //将dist设置为极大值 dist[s]:=0; flag[s]:=true; q[1]:=s; //s入队列 head:=1; tail:=1; while head<=tail do //while队列不为空 begin p:=q[head]; inc(head); //出队 flag[p]:=false; i:=first[p]; //找队首连出的边,首先是head[p] while i>-1 do //找所有的边直到没有下一条边 begin if dist[e[i].too]>max(dist[p],e[i].l) then //如果可以松弛 begin dist[e[i].too]:=max(dist[p],e[i].l); //松弛 if not flag[e[i].too] then //入队列 begin inc(tail); q[tail]:=e[i].too; flag[e[i].too]:=true; end; end; i:=e[i].nxt; //下一条 end; end; writeln(dist[t]); end. ``` 有什么不足,欢迎拍砖