最短路径算法
2015-07-22 20:41
429 查看
题目链接:A Walk Through the Forest
\qquad(2)对集合V−UV-U中各顶点的距离值进行修正:如果加入顶点vminv_{min}为中间顶点后,是v0v_0到viv_i的距离值比原来的距离值更小,则修改viv_i的距离值。
\qquad(3)重复(1)(2)中的操作,直到从v0v_0出发可以到达的所有顶点都在集合UU中为止。
邻接矩阵形式,复杂度为O(n2)O(n^2)(权值必须非负)
\quad2.迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)
\quad3.检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 dist[v]中。
\quadbellman-ford算法与Dijkstra算法的区别:Dijkstra算法在求解过程中,源点到集合S内各顶点的最短路径一旦求出,则之后不变了,修改的仅仅是源点到T集合中各顶点的最短路径长度。Bellman算法在求解过程中,每次循环都要修改所有顶点的dist[],也就是说源点到各顶点最短路径长度一直要到Bellman算法结束才确定下来。
\quadbellman-ford算法判断是否存在从源点可达的负权值回路的方法:在做完n-1次循环操作后,再对每条边 < u,v > 判断一下,加入这条边是否会使得顶点v的最短路径再缩短,即判断:dist[u]+w(u,v) < dist[v]是否成立,如果成立,则说明存在从源点可达的负权值回路。
算法复杂度为O(VE)O(VE),边的权值可以为负
\quad2.队列+松弛操作:读取队头顶点u,并将队头顶点u出队(记得消除标记);将与点u相连的所有点v进行松弛操作,如果能更新估计值(即令dist[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(记得标记),如果已经在队列中了,那么就不用入队以此循环,直到队空为止就完成了单源最短路的求解。
\quad负权回路判断:若一个点入队次数超过n,则有负权环。
算法平均复杂度为: O(KE)O(KE),K的平均值为2。(允许边权值为负值)
Floyd-Warshall算法(Floyd-Warshall algorithm)是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包。Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2)。
2.算法描述
\quad1)算法思想原理:
\quadFloyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释(这个诠释正是动态规划最富创造力的精华所在)
从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
\quad2).算法描述:
\quada.从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
\quadb.对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是更新它。
【问题】
\qquad求图中某点v0v_0到其它所有点的最短路径长度【Dijkstra算法】
\qquad(1)在集合V−UV-U中选择距离值最小的顶点vminv_{min}加入集合UU;\qquad(2)对集合V−UV-U中各顶点的距离值进行修正:如果加入顶点vminv_{min}为中间顶点后,是v0v_0到viv_i的距离值比原来的距离值更小,则修改viv_i的距离值。
\qquad(3)重复(1)(2)中的操作,直到从v0v_0出发可以到达的所有顶点都在集合UU中为止。
邻接矩阵形式,复杂度为O(n2)O(n^2)(权值必须非负)
#define MAX 1010 int dist[MAX]; //保存最短路径 int arcs[MAX][MAX]; //邻接矩阵 int pre[MAXN]; //保存前驱节点 void dijkstra(int n){ //迪杰斯特拉算法 arcs =1; dist = 0; for(int i=1; i<N; i++){ int min = INF,k=0; for(int j=1; j<=N; j++){ if(!arcs[j][j] && dist[j]<min){ //以邻接矩阵对角线上的元素作为该点是否被访问过的根据,节省空间 min = dist[j]; k = j; } } if(k == 0) continue; arcs[k][k] = 1; for(int j=1; j<=N; j++){ if(!arcs[j][j] && dist[j]>arcs[k][j]+dist[k]){ dist[j] = arcs[k][j]+dist[k]; //加入中间顶点后,更新当前图中其它点的最短距离 pre[j]=k; //保存前驱节点 } } } }
【bellman-ford算法】
\quad1.初始化:将除源点外的所有顶点的最短距离估计值 dist[v] ←+∞, dist[start] ←0;\quad2.迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)
\quad3.检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 dist[v]中。
\quadbellman-ford算法与Dijkstra算法的区别:Dijkstra算法在求解过程中,源点到集合S内各顶点的最短路径一旦求出,则之后不变了,修改的仅仅是源点到T集合中各顶点的最短路径长度。Bellman算法在求解过程中,每次循环都要修改所有顶点的dist[],也就是说源点到各顶点最短路径长度一直要到Bellman算法结束才确定下来。
\quadbellman-ford算法判断是否存在从源点可达的负权值回路的方法:在做完n-1次循环操作后,再对每条边 < u,v > 判断一下,加入这条边是否会使得顶点v的最短路径再缩短,即判断:dist[u]+w(u,v) < dist[v]是否成立,如果成立,则说明存在从源点可达的负权值回路。
算法复杂度为O(VE)O(VE),边的权值可以为负
#define INF 0x3f3f3f3f #define MAXN 100010 int dist[MAXN]; //保存最短路径 struct Edge{ int u; //边的起点 int v; //边的终点 int cost; //边的权值 }; vector<Edge> E; //边集 bool bellman_ford(int start, int n){ //点编号从从1开始 memset(dist,INF,sizeof(dist)); dist[start] = 0; for(int i=1; i<n; i++){ //最多做n-1次 bool flag = false; for(int j=0; j<E.size(); j++){ int u = E[j].u; int v = E[j].v; int cost = E[j].cost; if(dist[v] > dist[u]+cost){ dist[v] = dist[u]+cost; flag = true; } } if(!flag) //提前做完,无负权回路 return true; } for(int i=0; i<E.size(); i++) //检查是否有负权回路 if(dist[E[i].v] > dist[E[i].u]+E[i].cost) return false; return true; }
【SPFA算法】
\quad1.初始化: dist数组全部赋值为INF(无穷大);然后dist[start]=0; 表示源点不用求最短路径,或者说最短路就是0。将源点入队;(另外记住在整个算法中有顶点入队了要记得标记vis数组,cnt数组加1,有顶点出队了记得消除那个标记)\quad2.队列+松弛操作:读取队头顶点u,并将队头顶点u出队(记得消除标记);将与点u相连的所有点v进行松弛操作,如果能更新估计值(即令dist[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(记得标记),如果已经在队列中了,那么就不用入队以此循环,直到队空为止就完成了单源最短路的求解。
\quad负权回路判断:若一个点入队次数超过n,则有负权环。
算法平均复杂度为: O(KE)O(KE),K的平均值为2。(允许边权值为负值)
#define INF 0x3f3f3f3f #define MAXN 100010 int vis[MAXN]; //入队标志 int cnt[MAXN]; //入队次数 int dist[MAXN]; //最短路径 struct Edge{ int v; //边的终点 int cost; //边的权值 }; vector<Edge> E[MAXN]; //保存边集 bool SPFA(int start, int n){ memset(vis,0,sizeof(vis)); memset(dist,INF,sizeof(dist)); memset(cnt,0,sizeof(cnt)); vis[start] = 1; queue<int> que; while(!que.empty())que.pop(); que.push(start); cnt[start] = 1; while(!que.empty()){ int u = que.front(); que.pop(); vis[u] = 0; for(int i=0; i<E[u].size(); i++){ int v = E[u][i].v; if(dist[v] > dist[u]+E[u][i].cost){ dist[v] = dist[u]+E[u][i].cost; if(!vis[v]){ vis[v] = 1; que.push(v); if(++cnt[v] > n) return false; } } } } return true; }
【Floyd算法】
1.定义概览Floyd-Warshall算法(Floyd-Warshall algorithm)是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包。Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2)。
2.算法描述
\quad1)算法思想原理:
\quadFloyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释(这个诠释正是动态规划最富创造力的精华所在)
从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
\quad2).算法描述:
\quada.从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
\quadb.对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是更新它。
#define MAXN 10001 int path[MAXN][MAXN] //保存路径 int dist[MAXN][MAXN] //保存最短路径长度 void Floyd(int N){ //图中点的个数 memset(path,-1,sizeof(path)); for(int k=0; k<n; k++) for(int i=0; i<n; i++) for(int j=0; j<n; j++) if(A[i][j]>(A[i][k]+A[k][j])){ A[i][j]=A[i][k]+A[k][j]; path[i][j]=k; } }
相关文章推荐
- [Leetcode]-Unique Paths II
- 特殊文件: /dev/null和/dev/tty
- linux中软件包管理
- union的用法
- Android中WebView与JS的交互
- Android 关于.9.PNG 图片
- NYOJ 86 找球号(一)
- pascal笔记2
- poj1655解题报告(树的质心)
- 1090 加分二叉树
- 数据库
- Eclipse Java注释模板设置详解
- hdoj 1054 Strategic Game【匈牙利算法+最小顶点覆盖】
- Omega:flexible,scalable schedulers for large compute clusters论文理解
- 南阳oj NYOJ 寻找最大数 题目236
- poj 2676 Sudoku(数独)
- 向量及其运算(一):二维向量的基本运算
- 网页布局基础 第二次(盒子模型)
- bkwin设置文本控件为多行模式
- Android系统代码本地更新linux shell脚本1