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

  • 相关阅读:
    关于各种好玩的神奇函数
    模板——AC自动机
    模板——造数据
    VIM常用操作
    springboot注解
    面试题
    Linux常用命令
    Zookeeper
    对cpu与load的理解及线上问题处理思路
    top
  • 原文地址:https://www.cnblogs.com/sxmcACM/p/3346287.html
Copyright © 2011-2022 走看看