首页 > 代码库 > ACM-最短路(SPFA,Dijkstra,Floyd)之最短路——hdu2544

ACM-最短路(SPFA,Dijkstra,Floyd)之最短路——hdu2544

***************************************转载请注明出处:http://blog.csdn.net/lttree***************************************



最短路

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 28761    Accepted Submission(s): 12444


Problem Description
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?

 

Input
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。
 

Output
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间
 

Sample Input
2 1 1 2 3 3 3 1 2 5 2 3 5 3 1 2 0 0
 

Sample Output
3 2
 

题目:http://acm.hdu.edu.cn/showproblem.php?pid=2544


求最短路,我是看《挑战程序设计竞赛》里的书学的。

里面介绍了三种方法: Bellman-Ford、Dijkstra and Floyd

三者区别也都很明显:

Bellman-Ford:

求单源最短路,可以判断有无负权回路(若有,则不存在最短路), 时效性较好,时间复杂度O(VE)。

Bellman-Ford算法是求解单源最短路径问题的一种算法。

      单源点的最短路径问题是指: 给定一个加权有向图G和源点s,对于图G中的任意一点v,求从s到v的最短路径。

      与Dijkstra算法不同的是,在Bellman-Ford算法中,边的权值可以为负数。       设想从我们可以从图中找到一个环路(即从v出发,经过若干个点之后又回到v)且这个环路中所有边的权值之和为负。那么通过这个环路,环路中任意两点的最短路径就可以无穷小下去。如果不处理这个负环路,程序就会永远运行下去。 而Bellman-Ford算法具有分辨这种负环路的能力。


Dijkstra:

求单源、无负权的最短路。时效性较好,时间复杂度为O(V*V+E)。 源点可达的话,O(V*lgV+E*lgV)=>O(E*lgV)。       当是稀疏图的情况时,此时E=V*V/lgV,所以算法的时间复杂度可为O(V^2) 。若是斐波那契堆作优先队列的话,算法时间复杂度,则为O(V*lgV + E)。 

Floyd:

求多源、无负权边的最短路。用矩阵记录图。时效性较差,时间复杂度O(V^3)。        Floyd-Warshall算法(Floyd-Warshall algorithm)是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题。

Floyd-Warshall算法的时间复杂度为O(N^3),空间复杂度为O(N^2)。

      Floyd-Warshall的原理是动态规划: 设Di,j,k为从i到j的只以(1..k)集合中的节点为中间节点的最短路径的长度。 若最短路径经过点k,则Di,j,k = Di,k,k-1 + Dk,j,k-1;  若最短路径不经过点k,则Di,j,k = Di,j,k-1。  因此,Di,j,k = min(Di,k,k-1 + Dk,j,k-1 , Di,j,k-1)。

      在实际算法中,为了节约空间,可以直接在原来空间上进行迭代,这样空间可降至二维。 Floyd-Warshall算法的描述如下: for k ← 1 to n do   for i ← 1 to n do     for j ← 1 to n do       if (Di,k + Dk,j < Di,j) then         Di,j ← Di,k + Dk,j; 其中Di,j表示由点i到点j的代价,当Di,j为 ∞ 表示两点之间没有任何连接。

后来,我看Bellman-Ford的队列优化,SPFA(Shortest Path Faster Algorithm )。

SPFA:

是Bellman-Ford的队列优化,时效性相对好,时间复杂度O(kE)。(k<<V)。

与Bellman-ford算法类似,SPFA算法采用一系列的松弛操作以得到从某一个节点出发到达图中其它所有节点的最短路径。所不同的是,SPFA算法通过维护一个队列,使得一个节点的当前最短路径被更新之后没有必要立刻去更新其他的节点,从而大大减少了重复的操作次数。

      SPFA算法可以用于存在负数边权的图,这与dijkstra算法是不同的。

与Dijkstra算法与Bellman-ford算法不同,SPFA的算法时间效率是不稳定的,即它对于不同的图所需要的时间有很大的差别。

      在最好情形下,每一个节点都只入队一次,则算法实际上变为广度优先遍历,其时间复杂度仅为O(E)。另一方面,存在这样的例子,使得每一个节点都被入队(V-1)次,此时算法退化为Bellman-ford算法,其时间复杂度为O(VE)。

      SPFA算法在负边权图上可以完全取代Bellman-ford算法,另外在稀疏图中也表现良好。但是在非负边权图中,为了避免最坏情况的出现,通常使用效率更加稳定的Dijkstra算法,以及它的使用堆优化的版本。通常的SPFA算法在一类网格图中的表现不尽如人意。


然后,这道题我用了SPFA,Dijkstra和Floyd来做(Bellman-Ford 太慢,就不做了)

这道题,当时我怎么做,做不出来,后来发现是 MAX 设定为 0x7fffffff 问题。设置成别的大数就没事。

贡献了N个WA啊!!!

郁闷ING。。。


SPFA:

/****************************************
*****************************************
*        Author:Tree                    *
*From :http://blog.csdn.net/lttree      *
* Title : 最短路                       *
*Source: hdu 2544                       *
* Hint : SPFA                           *
*****************************************
****************************************/

#include <stdio.h>
#include <queue>
using namespace std;

#define RANGE 101
#define MAX 0x3f3f3f3f
int cost[RANGE][RANGE];
int d[RANGE];
bool used[RANGE];
int n,m;

void spfa( int s )
{
    int i,now;
    // 初始化
    for( i=1;i<=n;++i )
    {
        d[i]=MAX;
        used[i]=false;
    }

    d[s]=0;
    queue <int> q;
    q.push(s);
    used[s] = true;

    while(!q.empty())
    {
        now = q.front();
        q.pop();
        used[now] = false;
        for(i = 1; i <= n; i++)
        {
            if(d[i] > d[now] + cost[now][i])
            {
                d[i] = d[now] + cost[now][i];
                if(used[i] == 0)
                {
                    q.push(i);
                    used[i] = true;
                }
            }
        }
    }
}

int main()
{
    int i,j,A,B,C;
    while( scanf("%d%d",&n,&m) )
    {
        if( !n && !m )  break;
        // 初始化
        for( i=1;i<=n;++i )
            for( j=1;j<=i;++j )
                if( i==j )  cost[i][j]=0;
                else    cost[i][j]=cost[j][i]=MAX;

        for( i=0;i<m;++i )
        {
            scanf("%d%d%d",&A,&B,&C);
            cost[A][B]=cost[B][A]=C;
        }

        spfa(1);
        printf("%d\n",d[n]);
    }
    return 0;
}

Dijkstra:

/****************************************
*****************************************
*        Author:Tree                    *
*From :http://blog.csdn.net/lttree      *
* Title : 最短路                       *
*Source: hdu 2544                       *
* Hint : Dijkstra                       *
*****************************************
****************************************/

#include <stdio.h>
#define MAX 0x3f3f3f3f
#define RANGE 101

int cost[RANGE][RANGE];
int d[RANGE];
bool used[RANGE];

int n,m;
int Min( int a,int b )
{
    return a<b?a:b;
}

void Dijkstra( int s )
{
    int i,v,u;
    for( i=1;i<=n;++i )
    {
        used[i]=false;
        d[i]=cost[1][i];
    }
    d[s]=0;

    while( true )
    {
        v=-1;
        for( u=1;u<=n;++u )
            if( !used[u] && ( v==-1 || d[u]<d[v]) )
                v=u;
        if( v==-1 ) break;
        used[v]=true;

        for( u=1;u<=n;++u )
            d[u]=Min( d[u],d[v]+cost[v][u] );
    }
}

int main()
{
    int A,B,C,i,j;

    while( scanf("%d%d",&n,&m) )
    {
        if( !n && !m )  break;

        // 初始化
        for( i=1;i<=n;++i )
            for( j=1;j<=i;++j )
                if( i==j )  cost[i][j]=0;
                else    cost[i][j]=cost[j][i]=MAX;

        for( i=0;i<m;++i )
        {
            scanf("%d%d%d",&A,&B,&C);
            cost[A][B]=cost[B][A]=C;
        }

        Dijkstra(1);
        printf("%d\n",d[n]);
    }
    return 0;
}

Floyd:

/****************************************
*****************************************
*        Author:Tree                    *
*From :http://blog.csdn.net/lttree      *
* Title : 最短路                       *
*Source: hdu 2544                       *
* Hint : Floyd                          *
*****************************************
****************************************/

#include <stdio.h>
#define MAX 0x3f3f3f3f
#define RANGE 105

int d[RANGE][RANGE];
int n;

int Min( int a,int b )
{
    return a<b?a:b;
}
void warshall_floyd( void )
{
    int i,j,k;
    for( k=1;k<=n;++k )
        for( i=1;i<=n;++i )
            for( j=1;j<=n;++j )
                d[i][j]=Min( d[i][j],d[i][k]+d[k][j] );
}

int main()
{
    int m,A,B,C,i,j;

    while( scanf("%d%d",&n,&m) )
    {
        if( !n && !m )  break;

        // 初始化
        for( i=1;i<=n;++i )
            for( j=1;j<=i;++j )
            {
                if( i==j ) d[i][j]=0;
                else    d[i][j]=d[j][i]=MAX;
            }

        // 输入
        for( i=0;i<m;++i )
        {
            scanf("%d%d%d",&A,&B,&C);
            d[A][B]=d[B][A]=C;
        }

        // floyd算法求最短路
        warshall_floyd();
        printf("%d\n",d[1][n]);
    }
    return 0;
}