您的位置:首页 > 其它

最短路径算法

2015-07-22 20:41 429 查看
题目链接:A Walk Through the Forest

【问题】

\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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: