zoukankan      html  css  js  c++  java
  • 最短路算法(小小总结一下)

     

    1, Dijkstra算法

    (1) 处理正边权可以处理负边权,但必须是负边只存在源点s连出去的边

    (2) 时间复杂度……O(V^2)

    例题:

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

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

    Code:

    #include<iostream>

    #include<stdio.h>

    #include<iomanip>

    using namespace std;

    #define N 10000

    #define MAX 100000099

    int a[N][N];

    int dist[N];

    void input (int n,int m)

    {

        int p,q,len,i,j;

        for( i=1;i<=n;i++)

        {

            for(j=1;j<=n;j++)

                a[i][j]=MAX;

            dist[i]=MAX;

        }

        for(i=0;i<m;i++)

        {

            cin>>p>>q>>len;

            if(len<a[p][q])

            {

                a[p][q]=len;

                a[q][p]=len;

            }

        }

    }

    void dijkstra(int n)

    {

        int s[N],newdist;

        for(int i=1;i<=n;i++)

        {

            dist[i]=a[1][i];

            s[i]=0;

        }

        dist[1]=0;

        s[1]=1;

        for(i=2;i<=n;i++)

        {

            int j,tem=MAX;

            int u=1;

            for(j=2;j<=n;j++)

                if(!s[j]&&dist[j]<tem)

                {

                    u=j;

                    tem=dist[j];

                }

                s[u]=1;

                for(j=2;j<=n;j++)

                {

                    if(!s[j]&&a[u][j]<MAX)

                    {

                        newdist=dist[u]+a[u][j];

                        if(newdist<dist[j])

                            dist[j]=newdist;

       

                    }

                }

        }

    }

    int main()

    {

        int n,m;

        while(scanf("%d%d",&n,&m),m||n)

        {

            input(n,m);

            dijkstra(n);

            cout<<dist[n]<<endl;

        }

        return 0;

    }

    2.Bellman-Ford算法

    如果原图中不存在负环:

    s可达的任意点v的最短路至多经过v1条边。对于这v1条边,第一次松弛后第一条边不可再松弛。第二次松弛后第二条边不可再松弛,…这样便证明了算法在没有负环的图上的正确性。

    如果原图中存在负环:

    显然对于负环中的任何一个点,它的最短路会是负无穷,所以松弛操作不会停止。算法返回false

    时间复杂度: 两个for循环O(VE)

    例题(hdu2544)

    Code:

    #include <stdio.h>

    #define MAX 1000000000

    int m, n, i, j ,k;

    int start, end, value;

    int dis[105], edge[105][105];

    int Min (int a, int b)

    {

        return a < b ? a : b;

    }

    void Bellman (int source)

    {

        for (i = 0; i <= n; ++i)

        {

            dis[i] = edge[1][i];

        }

        for (k = 2; k < n; ++k)

        {

            for (i = 1; i <= n; ++i)

            {

                for (j = 1; j <= n; ++j)

                {

                    dis[i] = Min(dis[i], dis[j] + edge[j][i]);

                }

            }

        }

        printf ("%d ", dis[n]);

    }

    int main (void)

    {

        while (scanf ("%d%d", &n, &m) && (m || n))

        {

            for (i = 0; i <= n; ++i)

            {

                for (j = 0; j <= n; ++j)

                {

                    edge[i][j] = MAX;

                }

            }

            for (i = 0; i < m; ++i)

            {

                scanf ("%d%d%d", &start, &end, &value);

                edge[start][end] = edge[end][start] = value;

            }

            Bellman (1);

        }

        return 0;

    }

    3.SPFA算法

     一个Bellman-Ford算法优化: 每次只用距离减小的点去更新其他点

    如何实现?队列!

     

    思路:

     Step 1:初始时所有点d[]值置INF,源点d[]0。将源点放进队列。

    Step 2:当队列不为空时每次从队列中取出队首,对队首的每条边进行松弛。将松弛后d[]值改变并且不在队列中的点加入队列

     

    两点说明:

    时间复杂度

    最坏 : O(VE)一般 : O(kE)

    如何判负环?

    对每个点记录一个num值,表示被更新了多少次,如果某个点被更新的次数超过n-1次,则有负环

     

    例题(1544)

    Code:

    #include <stdio.h>

    #define MAX 1000000000

     

    struct Edge

    {

        int start;

        int end;

        int next;

        int value;

    } eg[20005];

     

    int m, n, i, pot;

    int startNode, endNode, value;

    int first, tail, popNumber;

    int dis[105], head[105], queue[30000];

    bool chose[200];

     

    void Build (int st, int nd, int val)

    {

        eg[pot].start = st;

        eg[pot].end = nd;

        eg[pot].value = val;

        eg[pot].next = head[st];

        head[st] = pot++;

    }

     

    void BFS (int x)

    {

        int k;

        for (k = head[x]; k != -1; k = eg[k].next)

        {

            if (dis[eg[k].end] > dis[x] + eg[k].value)

            {

                dis[eg[k].end] = dis[x] + eg[k].value;

                if (chose[eg[k].end] == false)

                {

                    queue[tail++] = eg[k].end;

                    chose[eg[k].end] = true;

                }

            }

        }

    }

     

    void SPFA (void)

    {

        dis[1] = 0;

        first = tail = 0;

        queue[tail++] = 1;

        //printf ("first = %d ", queue[first]);

        chose[1] = true;

        while (tail - first != 0)

        {

            //printf ("first = %d ", queue[first]);

            popNumber = queue[first];

            chose[queue[first]] = false;

            ++first;

            BFS (popNumber);

        }

        printf ("%d ", dis[n]);

    }

     

    int main (void)

    {

        while (scanf ("%d%d", &n, &m) && (m || n))

        {

            for (i = 0; i <= n; ++i)

            {

                head[i] = -1;

                dis[i] = MAX;

                chose[i] = false;

            }

            pot = 0;

            for (i = 0; i < m; ++i)

            {

                scanf ("%d%d%d", &startNode, &endNode, &value);

                Build (startNode, endNode, value);

                Build (endNode, startNode, value);

            }

            SPFA ();

        }

        return 0;

    }

    4.Floyd算法

    时间复杂度:  三重for循环  O(V^3)

     

    先看代码:

    for(k=1;k<=V;k++)

    for(i=1;i<=V;i++)

    for(j=1;j<=V;j++)

    if(dis[i][j]>dis[i][k]+dis[k][j])

    dis[i][j]=dis[i][k]+dis[k][j];    实际上是一个精巧的DP

    DP过程:

    dis[i][j][k]表示从ij的路径中,经过的点的编号不超过k的最短路

    边界条件:dis[i][j][0] = dis[i][j]

    转移方程:

    dis[i][j][k] = Min(dis[i][j][k-1] , dis[i][k][k-1] + dis[k][j][k-1])

    (k > 0 , 0 <= i , j <= n)

    答案:dis[i][j][n]

     

    例题(1544)

    Code:

    #include <stdio.h>

    int i, j, k, m, n, start, end, value;

    int map[200][200], dis[200][200];

    const int MAX = 1000000000;

    int Min (int a, int b)

    {

        return a < b ? a : b;

    }

    int main (void)

    {

        while (scanf("%d%d", &n, &m) && (m || n))

        {

            for (i = 0; i <= n; ++i)

            {

                for (j = 0; j <= n; ++j)

                {

                    map[i][j] = MAX;

                }

            }

            for (i = 0; i < m; ++i)

            {

                scanf ("%d%d%d", &start, &end, &value);

                map[start][end] = map[end][start] = value;

            }

            for (k = 1; k <= n; ++k)

            {

                for (i = 1; i <= n; ++i)

                {

                    for (j = 1; j <= n; ++j)

                    {

                        map[i][j] = Min (map[i][j], map[i][k] + map[k][j]);

                    }

                }

            }

            printf ("%d ", map[1][n]);

        }

        return 0;

    }

     

    <01>最短路算法的应用

    求无向图最小环

    最小环是指一个图中一个至少包含三个顶点的边权和最小的环

    Floyd

    设某环编号最大的顶点为 L 另两个顶点编号分别为 M N (M,N < L),则最大编号为 L 的最小环长度即为mp(M,L) + mp(N,L) + dp(M,N) ,其中 dp(M,N) 表示以 0L-1 号顶点为中间点时的最短路径,刚好符合 Floyd 算法最外层循环到 k=L 时的情况。

     

    <02>若为有向图呢?(CDOJ 1329)

    最短路算法的应用

    求下面不等式组的一组解:

    X1 - X2 <= 0 X1 - X5 <= -1 X2 - X5 <= 1 X3 - X1 <= 5 X4 - X1 <= 4 X4 - X3 <= -1 X5 - X3 <= -3 X5 - X4 <= -3

    Xi<=0(i=1,2,3,4,5)

    求完最短路后图中每条边

    dis(v) <= dis(u) + w(u, v)

    对于不等式Xi - Xj <= c,把它化成不等式:Xi <= Xj + c,就可以化成边Vj -> Vi,权值为c。最后,我们在这张图上求一次单源最短路径,这些不等式就会全部都满足

     

    <03>最短路算法的应用

    求下面不等式组的一组解:

    X1 - X2 <= 0 X1 - X5 <= -1 X2 - X5 <= 1 X3 - X1 <= 5 X4 - X1 <= 4 X4 - X3 <= -1 X5 - X3 <= -3 X5 - X4 <= -3

    Xi<=0(i=1,2,3,4,5)

    对于最后一个Xi<=0怎么办?

    新添一个X0=0,并使Xi-X0<=0!

    最后以X0为源点,跑最短路

    最后每个点的dis值就为答案

    注意:由于有负边权,不能使用dijkstra

  • 相关阅读:
    Uva 10779 collector's problem
    poj 2728 最优比率树(最小生成树问题)
    LA 3126 二分图匹配 最小路径覆盖
    poj 1149 最大流构图
    Step By Step(Java XML篇)
    Step By Step(Java 输入输出篇)
    Step By Step(Java 集合篇)
    Step By Step(Java 线程篇)
    Step By Step(Java 反射篇)
    Step By Step(Java 国际化篇)
  • 原文地址:https://www.cnblogs.com/sxmcACM/p/3346287.html
Copyright © 2011-2022 走看看