zoukankan      html  css  js  c++  java
  • 图论:HDU2544-最短路(最全、最经典的最短路入门及小结)

    最短路

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


    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
     

    Source
     

    Recommend
    lcy   |   We have carefully selected several similar problems for you:  2066 1874 1217 2112 1142 




    解题心得:
    1、代码主要是写写最短路的模板,包括dij(n^2,和nlogn),Floyd,spfa,包括一些优化。还是看代码吧,代码上有注释。

    2、下面说说几种计算最小路径的方法的优劣。

    关于dij算法主要就是一个松弛的方法外加上一个贪心的思想,它的实现原理原理可以看成三角形两边之和大于第三边,它是从起点找到距离它最近的一个点,那么这个当前最短最短边一定是最短距离了,因为如果从其他的边去走会出线至少两条比最短边长的边之和,和上图的展示效果相同,也正是这个特性dij算法不能够计算带有复权边的图,带有复权边只有就不符合三角形定理了,两边之和会小于第三遍边,只不过使用代码实现上面的图的过程。然而就经典的dij算法占用的时间和空间的复杂多过于大了,所有才有了后面的使用优先队列和堆优化。而在空间方面的优化也可以使用vector动态数组,但是如果出题人卡你的stl就可以使用邻接表关于vector和邻接表可以看看后面的代码。其实说白了dij算法就是一个A*算法,有空的可以去看看A*算法。(dij的优化有很多,不懂得可以看看代码,代码看不懂的去网上看看详解)。


    而floyd算法是在面对非单源最短路的情况下使用,因为使用floyd算法可以得到任意两个点之间的最短路径。Floyd的原理其实是一个动态规划,例如从a到b的距离可以在a到b之间找一个点从,看是否从a到c再从c到b的距离小于直接从a到达b。如果是则将a到b的更短的距离替换成当前距离逐步得到最短的距离。但是Floyd的复杂度是O(n^3)在一般情况下都会超时,但是floyd可以在一些情况下对数据进行处理(例如,与处理中),在每次使用floyd算法的时候最好都先计算一下复杂度再使用。


    spfa算法可以看成一个bfs,从起点开始搜索,然后层层松弛,spfa时间复杂度是O(VE,但是如果图中存在复权的环那么会一直压入,不停地找到更小的值,所以在这个时候可以判断一下,如果一条边压入次数超过了n次,也就是节点的次数,这时可以直接判断不可能因为有复权环。但是就spfa算法来说是可以计算带有复权边的图的,但是不能带有复权环。


    普通的dij    n^2的复杂度
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 100+10;
    const int Max = 99999999;
    int d[1000010];
    int maps[maxn][maxn];
    int n,m;
    bool use[maxn];//用于标记,避免循环走
    
    void Ds()
    {
        while(1)
        {
            int v = -1;//记录最近的点
            for(int i=1;i<=n;i++)
            if((v == -1 || d[i]<d[v]) && !use[i])
                v = i;
            if(v == -1)
                break;
            use[v] = 1;
            for(int i=1;i<=n;i++)
                d[i] = min(d[i],d[v]+maps[i][v]);//d[v] + maps[i][v],记录从起点到i的距离
        }
    }
    int main()
    {
        //printf("%d",Max);
        while(scanf("%d%d",&n,&m) && n+m)
        {
            for(int i=0;i<maxn;i++)
                for(int j=0;j<maxn;j++)
                    maps[i][j] = Max;//初始化为最大
            memset(use,0,sizeof(use));
            for(int i=0;i<=m*n+10;i++)
                d[i] = Max;//初始化为最大
            for(int i=0;i<m;i++)
            {
                int start,end;
                int len;
                d[1] = 0;//自身走到自身是0
                scanf("%d%d%d",&start,&end,&len);
                maps[start][end] = maps[end][start] = len;//是双向的并没有固定方向
            }
            Ds();
            printf("%d
    ",d[n]);
        }
    }


    dij算法优先队列的优化和空间的处理
    /*
    这个主要优化的是空间存放问题和时间优化,这个算是真正实用的dij
    关于空间优化是直接使用vect 动态数组,毕竟每次都开静态的二维数组不现实
    然后就是使用优先队列,使用优先队列在松弛的时候可以直接找到最短的额那个
    边这样就节省了找最短边的过程。
    */
    
    
    
    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 1005
    #define INF 0x3f3f3f
    struct node
    {
        int now,len;
        bool operator <(const node &a) const
        {
            return a.len<len;
        }
    };
    int n,m,dis[maxn],use[maxn];
    vector<node> mp[maxn];//使用动态数组存放,可以避免很大的空间浪费
    void dij(int st)
    {
        priority_queue<node> qu;
        node tmp;
        tmp.now=st;
        tmp.len=0;
        qu.push(tmp);
    
        dis[st]=0;
        while(qu.size())
        {
            tmp=qu.top();
            qu.pop();
            if(use[tmp.now]==1)
                continue;
            use[tmp.now]=1;
    
            int l=mp[tmp.now].size();
            for(int i=0;i<l;i++)
            {
                if(dis[mp[tmp.now][i].now]>dis[tmp.now]+mp[tmp.now][i].len)
                {
                    dis[mp[tmp.now][i].now]=dis[tmp.now]+mp[tmp.now][i].len;
                    node next;
                    next.now=mp[tmp.now][i].now;
                    next.len=dis[next.now];
                    qu.push(next);
                }
            }
        }
    }
    int main()
    {
        while(scanf("%d%d",&n,&m),n|m)
        {
            for(int i=1;i<=n;i++)
            {
                dis[i]=INF;
                mp[i].clear();
            }
            memset(use,0,sizeof(use));
    
            node tmp;
            for(int i=1;i<=m;i++)
            {
                int x,y,len;
                scanf("%d%d%d",&x,&y,&len);
                tmp.now=y,tmp.len=len;
                mp[x].push_back(tmp);
                tmp.now=x;
                mp[y].push_back(tmp);
            }
            dij(1);
            printf("%d
    ",dis[n]);
        }
        return 0;
    }




    Floyd算法   N^3的复杂度
    /*
    floyd算法可以算出任意两个点之间的最小距离,但是要注意的是floyd
    的时间复杂度是n^3数据量比较大的时候很容易超时,一般不会直接使用
    floydfloyd只是用来处理一些数据
    */
    
    
    #include<bits/stdc++.h>
    using namespace std;
    int n,m;
    const int maxn = 110;
    const int INF = 0x3f3f3f3f;
    int maps[maxn][maxn];
    
    void pre_maps()
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                maps[i][j] = INF;
        while(m--)
        {
            int a,b,len;
            scanf("%d%d%d",&a,&b,&len);
            if(len < maps[a][b])
                maps[a][b] = maps[b][a] = len;
        }
    }
    
    void Floyd()
    {
        for(int k=1;k<=n;k++)//这里要注意一下,作为中间的那个点必须要放在最外层
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    if(maps[i][j] > maps[i][k] + maps[k][j])
                        maps[i][j] = maps[i][k] + maps[k][j];
    }
    
    int main()
    {
        while(scanf("%d%d",&n,&m) && (n+m))
        {
            pre_maps();
            Floyd();
            printf("%d
    ",maps[1][n]);
        }
        return 0;
    }



    SPFA算法算法复杂度和优化过的dij差不多。
    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 1005
    #define INF 0x3f3f3f
    struct node
    {
        int to,len;
    };
    int n,m,use[maxn],dis[maxn];//这里use是记录这个点是否在队列中,如果在队列中就不再压入了除了队列要将标记消除
    vector<node> mp[maxn];
    void spfa(int st)
    {
        dis[st]=0;
        use[st]=1;
        queue<int> qu;
        qu.push(st);
        int now,next;
        while(qu.size())
        {
            now=qu.front();
            qu.pop();
            use[now]=0;
            int l=mp[now].size();
            for(int i=0;i<l;i++)
            {
                next=mp[now][i].to;
                int len=dis[now]+mp[now][i].len;
    
                if(dis[next]>len)
                {
                    dis[next]=len;
                    if(use[next]==0)
                    {
                        qu.push(next);
                        use[next]=1;
                    }
                }
            }
        }
    }
    int main()
    {
        while(scanf("%d%d",&n,&m),n|m)
        {
            for(int i=1;i<=n;i++)
            {
                dis[i]=INF;
                mp[i].clear();
            }
            memset(use,0,sizeof(use));
            node now;
            for(int i=1;i<=m;i++)
            {
                int x,y,len;
                scanf("%d%d%d",&x,&y,&len);
    
                now.to=y,now.len=len;
                mp[x].push_back(now);
                now.to=x;
                mp[y].push_back(now);
            }
            spfa(1);
            printf("%d
    ",dis[n]);
        }
        return 0;
    }


  • 相关阅读:
    卖家必须了解的库存数据,亚马逊库存报告都帮你整理好了
    Genymotion 无法安装 APK 解决方案
    这张系统架构图画的漂亮!
    IT项目风险大全
    ElasticSearch是一个基于Lucene的搜索服务器
    商业模式的定义、商业模式的好坏
    如何实现互联网+业务与IT的融合
    UBUNTU PHP 版本切换
    Ubuntu为PHP安装SOAP扩展
    实现Linux下的ls -l命令
  • 原文地址:https://www.cnblogs.com/GoldenFingers/p/9107321.html
Copyright © 2011-2022 走看看