最短路算法(小小总结一下)

 

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\n", 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\n", queue[first]);

    chose[1] = true;

    while (tail - first != 0)

    {

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

        popNumber = queue[first];

        chose[queue[first]] = false;

        ++first;

        BFS (popNumber);

    }

    printf ("%d\n", 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\n", 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

posted @ 2013-09-29 20:20  天天AC  阅读(338)  评论(0编辑  收藏  举报