zoukankan      html  css  js  c++  java
  • 最短路径四种方法

    例题:HDU 2544

    最短路
    Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 89730    Accepted Submission(s): 38892


    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
     

    1),深度或广度优先搜索算法(解决单源最短路径)
    从起始结点开始访问所有的深度遍历路径或广度优先路径,则到达终点结点的路径有多条,取其中路径权值最短的一条则为最短路径。
    给定一个带权有向图G=(V,E),其中每条边的权是一个实数。另外,还给定V中的一个顶点,称为
    源。
    现在要计算从源到其他所有各顶点的最短路径长度。这里的长度就是指路上各边权之和。这个问题通
    常称为单源最短路径 [1] 问题。
    从起始结点开始访问所有的深度遍历路径或广度优先路径,则到达终点结点的路径有多条,取其中路
    径权值最短的一条则为最短路径

    下面是核心代码:

    //题意:求1->n的最短路径
    #include<iostream>
    #include<string.h>
    #define inf 99999999
    using namespace std;
    int dis[111][111];
    bool vis[111];
    int n,cnt;//n为节点数,cnt为最短长度
    void init(int x){
             for(int i=0;i<=n;i++){
                      for(int j=0;j<=n;j++)
                               dis[i][j]=inf;
                      dis[i][i]=0;
                      vis[i]=0;
             }
    }
    void dfs(int st,int dst)
    {
             if(dst>cnt)return ;//距离大于最短路径,无需遍历
             if(st==n){//到达终点
                      cnt=cnt>dst?dst:cnt;
                      return;
             }
             for(int i=1;i<=n;i++)
             {
                      if(!vis[i]&&dis[st][i]!=inf&&dis[st][i]){
                               vis[i]=1;
                               dfs(i,dst+dis[st][i]);
                               vis[i]=0;
                      }
             }
    }
    int main()
    {
             int m;
             while(~scanf("%d%d",&n,&m)&&n&&m)
             {
                      int x,y,len;
                      cnt=inf;
                      init(n);
                      while(m--){
                               scanf("%d%d%d",&x,&y,&len);
                               dis[x][y]=min(dis[x][y],len);//两点之间距离重复输入取小距离
                               dis[y][x]=dis[x][y];
                      }
                      vis[1]=1;
                      dfs(1,0);
                      printf("%d
    ",cnt);
             }
             return 0;
    }
    Sample Input 2
    5 14
    2 2 262
    5 3 403
    4 2 456
    1 5 289
    3 1 1000
    2 4 217
    2 5 536
    2 5 415
    2 4 880
    3 1 179
    3 4 972
    5 3 2
    1 3 491
    4 1 872
    0 0
    Sample Output 2
    181

    2),弗洛伊德算法(解决多源最短路径):时间复杂度O(n^3),空间复杂度O(n^2)
    基本思想:最开始只允许经过1号顶点进行中转,接下来只允许经过1号和2号顶点进行中转......允许经过1~n号所有顶点进行中转,来不断动态更新任意两点之间的最短路程。即求从i号顶点到j号顶点只经过前k号点的最短路程。

    //题意:求1->n的最短路径
    #include<iostream>
    #include<string.h>
    #define inf 99999999
    using namespace std;
    int n,dis[111][111];
    void init(){
             for(int i=0;i<=n;i++){
                      for(int j=0;j<=n;j++)
                               dis[i][j]=inf;
                      dis[i][i]=0;
             }
    }
    int main()
    {
             int m;
             while(~scanf("%d%d",&n,&m)&&n&&m)
             {
                      init();
                      while(m--){
                               int x,y,len;
                               scanf("%d%d%d",&x,&y,&len);
                               dis[x][y]=min(dis[x][y],len);
                               dis[y][x]=dis[x][y];
                      }
                      for(int k=1;k<=n;k++)//要经过的点
                               for(int i=1;i<=n;i++)
                                        for(int j=1;j<=n;j++)
                                                 dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
                      printf("%d
    ",dis[1][n]);//可以选任意两点之间的距离
             }
             return 0;
    }
    Sample Input 2
    5 14
    2 2 262
    5 3 403
    4 2 456
    1 5 289
    3 1 1000
    2 4 217
    2 5 536
    2 5 415
    2 4 880
    3 1 179
    3 4 972
    5 3 2
    1 3 491
    4 1 872
    0 0
    Sample Output 2
    181

    3),迪杰斯特拉算法(解决单源最短路径)
    基本思想:每次找到离源点(如1号结点)最近的一个顶点,然后以该顶点为中心进行扩展,最终得到源点到其余所有点的最短路径。

    基本步骤:1.开容器v,储存子节点、距离、花费;2、开数组dis记录起始点到各点距离;3、进行n-1次松弛操作(先找出未标记点中离起始点最近的点,标记该点,然后求出该点子节点到起始点的最短距离(优先)与最短花费);4、输出到终点的最短距离与花费;

    //题意:求两点之间最短路径
    #include<iostream>
    #include<string.h>
    #include<vector>
    #include<algorithm>
    #define N 999999999
    using namespace std;
    struct node{
             int er,len,cost;
    };
    vector<node>v[1111];
    int main()
    {
             int n,m;
             while(~scanf("%d%d",&n,&m)&&n&&m)
             {
                      int dis[1111],spend[1111];
                      bool vis[1111];
                      node tmp;
                      int x,y;
                      for(int i=0;i<1111;i++)
                               v[i].clear();
                      while(m--){
                               scanf("%d%d%d%d",&x,&y,&tmp.len,&tmp.cost);
                               tmp.er=x;
                               v[y].push_back(tmp);
                               tmp.er=y;
                               v[x].push_back(tmp);
                      }
                      scanf("%d%d",&x,&y);//起点和终点
                      for(int i=1;i<=n;i++){
                               vis[i]=0;
                               dis[i]=spend[i]=N;
                      }
                      for(int i=0;i<v[x].size();i++){
                               dis[v[x][i].er]=v[x][i].len;
                               spend[v[x][i].er]=v[x][i].cost;
                      }
                      vis[x]=1;
                      for(int k=1;k<=n-1;k++)
                      {
                               int id,mi=N;
                               for(int i=1;i<=n;i++){
                                        if(!vis[i]&&dis[i]<mi){//查询并记录离x最近的点
                                                 id=i;mi=dis[i];
                                        }
                               }
                               vis[id]=1;//标记过的点已经是最短
                               for(int i=0;i<v[id].size();i++)
                               {
                                        int vv=v[id][i].er;
                                        if(!vis[vv]&&dis[vv]>dis[id]+v[id][i].len)//未标记、直接距离大于通过id点的距离
                                                 dis[vv]=dis[id]+v[id][i].len,
                                                 spend[vv]=spend[id]+v[id][i].cost;
                                        else if(!vis[vv]&&dis[vv]==dis[id]+v[id][i].len&&spend[vv]>spend[vv]+v[id][i].cost)//未标记、距离相等找花费更小的
                                                 spend[vv]=spend[id]+v[id][i].cost;
                               }
                      }
                      printf("%d %d
    ",dis[y],spend[y]);
             }
             return 0;
    }
    /*
    3 2
    1 2 5 6
    2 3 4 5
    1 3
    3 2
    1 3 5 6
    2 1 3 5
    3 2
    
    9 11
    8 11
    */
    

    4),Bellman-Ford算法(解决负权边,解决单源最短路径,前几种方法不能求含负权边的图)::时间复杂度O(nm),空间复杂度O(m)
    主要思想:对所有的边进行n-1轮松弛操作,因为在一个含有n个顶点的图中,任意两点之间的最短路径最多包含n-1边。换句话说,第1轮在对所有的边进行松弛后,得到的是从1号顶点只能经过一条边到达其余各定点的最短路径长度。第2轮在对所有的边进行松弛后,得到的是从1号顶点只能经过两条边到达其余各定点的最短路径长度,......
    以下是图示:

    此外,Bellman_Ford还可以检测一个图是否含有负权回路:POJ1860

    /*
    题意:有多种汇币,汇币之间可以交换,这需要手续费,当你用100A币
    交换B币时,A到B的汇率是29.75,手续费是0.39,那么你可以得到
    (100 - 0.39) * 29.75 = 2963.3975 B币。问s币的金额经过交换最终
    得到的s币金额数能否增加
    货币的交换是可以重复多次的,所以我们需要找出是否存在
    正权回路,且最后得到的s金额是增加的
    怎么找正权回路呢?(正权回路:在这一回路上,顶点的权值能不断增加即能一直进行松弛)
    分析:
    反向利用Bellman-Ford算法
    单源最短路径算法,因为题目可能存在负边,所以用Bellman Ford算法,
    原始Bellman Ford可以用来求负环,这题需要改进一下用来求正环
    
    一种货币就是图上的一个点
    一个“兑换点”就是图上两种货币之间的一个兑换环,相当于“兑换方式”M的个数,是双边
    唯一值得注意的是权值,当拥有货币A的数量为V时,A到A的权值为K,即没有兑换
    而A到B的权值为(V-Cab)*Rab
    本题是“求最大路径”,之所以被归类为“求最小路径”是因为本题题恰恰
    与bellman-Ford算法的松弛条件相反,求的是能无限松弛的最大正权路径,
    但是依然能够利用bellman-Ford的思想去解题。
    因此初始化d(S)=V   而源点到其他店的距离(权值)初始化为无穷小(0),
    当s到其他某点的距离能不断变大时,说明存在最大路径
    */
    #include<iostream>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    struct node
    {
             int x,y;
             double r,c;
    }num[222];
    int n,m,s,ans;
    double v;
    void add(int x,int y,double r,double c)
    {
             num[ans].x=x;
             num[ans].y=y;
             num[ans].r=r;
             num[ans].c=c;
             ans++;
    }
    bool bellon()
    {
             double dis[111];
             for(int i=0;i<=n;i++)
                      dis[i]=0;
             dis[s]=v;
             for(int j=1;j<n;j++)
             {
                      bool flag=0;
                      for(int i=0;i<ans;i++){
                               if(dis[num[i].y]<(dis[num[i].x]-num[i].c)*num[i].r)
                                        dis[num[i].y]=(dis[num[i].x]-num[i].c)*num[i].r,flag=1;
                      }
                      if(!flag)
                               return 0;
             }
             for(int i=0;i<ans;i++)
                      if(dis[num[i].y]<(dis[num[i].x]-num[i].c)*num[i].r)
                               return 1;
             return 0;
    }
    int main()
    {
             int a,b;
             ans=0;
             double ra,rb,ca,cb;
             scanf("%d%d%d%lf",&n,&m,&s,&v);
             while(m--)
             {
                      scanf("%d%d%lf%lf%lf%lf",&a,&b,&ra,&ca,&rb,&cb);
                      add(a,b,ra,ca);
                      add(b,a,rb,cb);
             }
             if(bellon())
                      printf("YES
    ");
             else
                      printf("NO
    ");
             return 0;
    }
    
  • 相关阅读:
    Spring IoC详解
    Hibernate 和Mybatis的区别
    Nand Flash 驱动框架
    Nor Flash 驱动框架
    USB驱动框架
    输入子系统框架
    module_init 内核调用过程
    平台设备驱动框架
    LCD驱动框架
    嵌入式-开篇
  • 原文地址:https://www.cnblogs.com/aeipyuan/p/9893115.html
Copyright © 2011-2022 走看看