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;
    }
    
  • 相关阅读:
    bzoj 1017 魔兽地图DotR
    poj 1322 chocolate
    bzoj 1045 糖果传递
    poj 3067 japan
    timus 1109 Conference(二分图匹配)
    URAL 1205 By the Underground or by Foot?(SPFA)
    URAL 1242 Werewolf(DFS)
    timus 1033 Labyrinth(BFS)
    URAL 1208 Legendary Teams Contest(DFS)
    URAL 1930 Ivan's Car(BFS)
  • 原文地址:https://www.cnblogs.com/aeipyuan/p/9893115.html
Copyright © 2011-2022 走看看