图论:单源最短路与多源最短路问题

转载自http://acm.uestc.edu.cn/bbs/read.php?tid=5670

下载ppt帐号:qscqesze

密码:123456

-------------------------------------------------------------------

单源最短路径:

松弛操作:D[i]表示源点s到i的当前最短路径
1.条件:d[i]+e[i][j]<d[j]
2.更新:d[j]=d[i]+e[i][j]

 

 

 

Dijkstra算法:

算法初始时d[s] = 0,其余的点的d[]值为INF。有S、T两个集合,S集合中包含所有已求得最短路的点,T集合包含未求得最短路的点。
当T集合非空时,从T集合中选取d[]值最小的一个点,如果该点的d[] !=INF,则把它放入S集合,此时d[]值就是该点的最短路值。
当T集合为空或从T中选出的点的d[]==INF时,算法结束。

 

void dijkstra(int s,int t) {
    初始化S={空集}, T=全集-S;
    d[s] = 0; 其余d值为INF
    while (T非空 && T中最小的d[] != INF)
    {
        取出T中具有最小d[]的点i;
        for (所有不在S中且与i相邻的点j)
          if (d[j] > d[i] + cost[i][j]) d[j] = d[i] + cost[i][j]; ( “松弛”操作” )
        S = S + {i}; //把i点添加到集合S里
        T = T – {i};
    }
    return;
}

 

关于算法的说明:

每次选取的j使d[]最小,这样可以保证它的路径已经是最短路。因为如果经过某个未标记点p到达j会产生更短的路,那么到达j的最短路长度必然要加上一个更大的d [p],矛盾

d [k]=min{d [k],d [j]+cost[j][k]}的作用是在标记j以后更新d数组(松弛操作)

每次循环会对1个点进行标记,所以n-1次循环后所有点都做标记,d[]的含义变成可以经过所有点的最短距离,就是我们要的最短距离

如果找到的d[]最小的一个是d[]=无穷大,则可以提前结束循环,未做标记的点都是不可到达的

Dijkstra只能针对正权

所有边权非负
单源最短路
Dijkstra的本质是基于贪心的思想

反例:

Dijkstra复杂度:
最朴素的实现O(V^2)
堆实现O(ElogV)

Dijkstra堆实现:

用堆实现Dijkstra算法可以将复杂度降至O( ElogV);
用堆来维护T集合中的点,则在选取T集合中具有最小d[]值的点的复杂度为O(1),而每次在堆中维护一个点的复杂度为O(logV),每条边可能进行一次维护。所以总复杂度是O(ElogV);
当E<<V*V时,用堆实现将会快很多。

 

 

 SPFA算法(Shortest Path Faster Algorithm)

SPFA算法用队列来保存可能做松弛操作的节点。
初始时所有点d[]值置INF,源点d[s]为0。将源点放进队列。
当队列不为空时每次从队列中取出队首节点,对该节点发出的每条边进行松弛。将松弛后d[]值改变并且不在队列中的点加入队列。

 

void spfa(int s){ 
  for(int i = 1; i <= n; ++i) d[i] = INF; //所有点的d[]置无穷
       d[s] = 0;            // 源点d[]置0
       q.push(s);
    while(!q.empty()){
         int x = q.front(); q.pop();
         for all edge(x, y, w)
            if(d[y] > d[x] + w){
                d[y] = d[x] + w;
                if(y不在q中) {
                    q.push(y);
                }
            }
    }
}

 

示例;

 

全局最短路径

Floyd-warshell:

求所有点对的最短路径
DP
设d[i][j][k]表示从i到j的路径中,经过的点的编号不超过k的最短路。
边界条件d[i][j][0] = dis[i][j],d[i][i]=0,余下d[i][j]=INF;
转移方程:
d[i][j][k] = Min(d[i][j][k-1] , d[i][k][k-1] + d[k][j][k-1]) (k > 0 , 0 <= i , j <= n)
则dp[i][j][n]即为所求

 

for(k=1;k<=n;++k)
    for(i=1;i<=n;++i)
        for(j=1;j<=n;++j)
        if(d[i][j]>d[i][k]+d[k][j])
            d[i][j]=d[i][k]+d[k][j];
            
        

 

 

 

 

算法时间复杂度为O(n^3)
原程序实际上是这个DP的一个精巧的实现,省略了最后一维数组
因为在第k次进行更新时,只会用d[u][k], d[k][u]对别的 d[][]值进行更新,而d[u][k]和d[k][u]不会改变。

 

Floyd vs Spfa
 

posted @ 2015-05-28 12:10  qscqesze  阅读(1588)  评论(0编辑  收藏  举报