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;
    }


  • 相关阅读:
    重新想象 Windows 8 Store Apps (15) 控件 UI: 字体继承, Style, ControlTemplate, SystemResource, VisualState, VisualStateManager
    重新想象 Windows 8 Store Apps (12) 控件之 GridView 特性: 拖动项, 项尺寸可变, 分组显示
    返璞归真 asp.net mvc (10) asp.net mvc 4.0 新特性之 Web API
    与众不同 windows phone (29) Communication(通信)之与 OData 服务通信
    与众不同 windows phone (33) Communication(通信)之源特定组播 SSM(Source Specific Multicast)
    与众不同 windows phone (27) Feature(特性)之搜索的可扩展性, 程序的生命周期和页面的生命周期, 页面导航, 系统状态栏
    与众不同 windows phone (30) Communication(通信)之基于 Socket TCP 开发一个多人聊天室
    返璞归真 asp.net mvc (12) asp.net mvc 4.0 新特性之移动特性
    重新想象 Windows 8 Store Apps (2) 控件之按钮控件: Button, HyperlinkButton, RepeatButton, ToggleButton, RadioButton, CheckBox, ToggleSwitch
    重新想象 Windows 8 Store Apps (10) 控件之 ScrollViewer 特性: Chaining, Rail, Inertia, Snap, Zoom
  • 原文地址:https://www.cnblogs.com/GoldenFingers/p/9107321.html
Copyright © 2011-2022 走看看