图论之 最短路
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
图论之最短路
一、求最短路方法(对于一个包含环的图)
1、Dijkstra
2、Bellman-ford
3、SPFA
4、Floyd
二、Dijkstra思想(求单源点最短路,不含负边权)
1、设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径, 就将其加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v 到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。
2、Dijkstra步骤
(1)初始时,S只包含源点,即S=v,距离为0。U包含除v外的其他顶点,U 中顶点u距离为边上的权;
(2)从U中选取一个距离v最小的顶点k,把k加入S中(该选定的距离就是v到k的最短路径长度);
(3)以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值为经过顶点k的值(松弛操作);
(4)重复步骤(2)和(3)直到所有顶点都包含在S中。
3、Dijkstra两种实现方法
(1)邻接矩阵+找最小边
(2)邻接表+优先队列
关键:松弛操作
if(d[v]>d[u]+e[u][v].w)
d[v]=d[u]+e[u][v].w
4、Dijkstra 稠密图的邻接矩阵
for(int i=0;i { int x,m=inf; for(int y=0;y m=dis[y];x=y; vis[x]=1; for(int y=0;y dis[y]=dis[x]+w[x][y]; } 5、邻接链表+优先队列 memset(dis,127,sizeof(dis)); dis[1]=0; q.push(make_pair(dis[1],1)); while(!q.empty()) { int u=q.top().second;q.pop(); if(vis[u]) continue; vis[u]=1; for(int k=head[u];k!=-1;k=e[k].next) { if(dis[e[k].v]>dis[u]+e[k].w) { dis[e[k].v]=dis[u]+e[k].w; q.push(make_pair(dis[e[k].v],e[k].v)); } } } 6、路径输出 方法一:从终点出发,不断顺着dis[y]==dis[x]+w[x][y]的边从y回到x,直到回到起点,但更好的方法是 方法二:在更新时维护father指针 for(int y=0;y if(dis[y]>dis[x]+w[x][y]) father[y]=x; 7、邻接表+优先队列的dijkstra (1)根据算法,我们需要在找到最短dis的节点序号v,所以入队的一个元素包含dis和v两部分,那么我们使用pair将捆绑两个整形:typedef pair (3)注意:此时在元素入队时要加make_pair。如将(d[1],0)入队:q.push(make_pair(d[1],0)); (4)邻接矩阵的算法显然是O(N^2)的 稀疏图的邻接表 如果一个图的顶点很多,那它往往是稀疏图,那么使用邻接表和优先队列可以优化到O(MlogN)。 三、Bellman-ford(思想求单源点最短路,可含负边权) 1、算法步骤: (1)初始化:将除源点外的所有顶点最短距离估计值d[v]=inf,d[s]=0; (2)迭代求解:反复对边集E中每条边进行松弛操作,使得顶点集V中每个顶点v的最短距离估计值逐步逼近其最短距离; (3)检验负权回路:如果有存在点v,使得d[v]>d[u]+w[u][v],则有负权回路,返回false; (4)返回true,源点到v的最短距离保存在d[v]中。 2、伪代码 bool bellman-ford(G,w,s) { for each vertex in V(G)d[v]=inf;d[s]=0; for(i=1;i for if(d[v]>d[u]+w[u][v])d[v]=d[u]+w[u][v]; for each edge(u,v) in E(G)//v-1次松弛结束若还可以松弛,则有负环 if(d[v]>d[u]+w[u][v])return false; return true; } 四、SPFA思想(使用队列优化后的Bellman-ford) 1、用一个队列来进行维护。 流程:初始时,将源点入队,每次从队列中取出一个元素,并对其所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队,直到队列为空时算法结束。 可以证明用队列维护的Bellman-Ford最坏情况下时间为O(nm),通常时间为O(km),其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。2、伪代码 q.push(s);vis[s]=1; void spfa() { while(!q.empty()) u=q.front();q.pop();vis[u]=0;//出队标记 for each v in adj(u) {if(dis[v]>d[u]+w[u][v]) { dis[v]=d[u]+w[u][v]; if(!vis[v]){q.push(v);vis[v]=1;} } } 五、floyd思想(求各点间的最短路,可含负边权) 1、动态规划原理 设d(i,j)为从i到j的只以(1…k)集合中的节点为中间点的最短路的长度; (1)若经过k,d(i,j)=d(i,k)+d(k,j) (2)若不经过k(可能经过1~k-1中的点),d(i,j)=d(i,j) 则d(i,j)=min(d(i,k)+d(k,j), d(i,j)) 2、伪代码 for(k=0;k for(i=0;i for(j=0;j if(d[i][j]>d[i][k]+d[k][j]) d[i][j]= d[i][k]+d[k][j]; 注:初始时,d[i][i]=0,其他d值为inf 六、三种最短路算法 1、dijkstra(单源点最短路):贪心的思想,每次从集合U中找一个离源点最近的点加入到集合S中,并以新加入的点作为中间点松弛U中的点到源点的距离。直到U为空算法结束。 使用优先队列优化。队列中存储的是U中点的子集。 不能处理负权存在的情况。 复杂度远小于O(M*N),因为贪心所以速度三者中最快,用二项堆优化到O(ElogV)。