码迷,mamicode.com
首页 > 其他好文 > 详细

单源最短路径Dijkstra、BellmanFord、SPFA【模板】

时间:2015-05-04 20:15:51      阅读:196      评论:0      收藏:0      [点我收藏+]

标签:

Dijkstra算法:
将所有点分为两个集合。如果源点s到u的最短路径已经确定,点u就属于集合Va,否则属于集合Vb。
1.将源点s到图中各点的直接距离当做初始值记录为s到各点的最短距离,不能到达的记为INF。S到S距离为0。
2.在集合Vb中的点中找一个点u,使得源点s到该点u路径长度最短,将u从Vb中除去,加到V1中。这时候求出了当前S到u的最短路径。
3.把新确定的点u更新s到集合Vb中每一个点v的距离,如果s到u的距离加上u到v的直接距离小于当前s到v的距离,则表示新找到的最短路径长度比之前的更短,那么更新这个距离,并更新最短路径。
4.重复步骤2.3,直到集合Vb中已经没有点,或是Vb没有从源点s能达到的点。
如果有带花费的最短路径,建立一个二维数组cost[][]表示花费图,建立一维数组Value[]来更新最小花费。则对于每个与k相邻的在Vb中的点j,更新s到j的最短距离时,如果距离相等,则更新花费。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 110;
const int INF = 1000000000;

int Map[MAXN][MAXN],pre[MAXN],Dist[MAXN];
bool vis[MAXN];
//Map[]来存储图,pre[]来保存结点前驱、源点、终点
//Dist[i]为源点s到节点i的最短距离
//vis[i]记录点i是否属于集合Va
void Dijkstra(int N,int s)
{
    int Min;
    for(int i = 1; i <= N; ++i) //初始化
    {
        vis[i] = false;
        if(i != s)
        {
            Dist[i] = Map[s][i];
            pre[i] = s;
        }
    }
    Dist[s] = 0;
    vis[s] = true;
    for(int i = 1; i <= N-1; ++i) //循环N-1次,求源点s到其他N-1个点最短路径
    {
        Min = INF;
        int k = 0;
        for(int j = 1; j <= N; ++j) //在Vb中的点钟取一个s到其距离最小的点k
        {
            if(!vis[j] && Dist[j] < Min)
            {
                Min = Dist[j];
                k = j;
            }
        }
        if(k == 0)  //如果没有点可以扩展,即剩余的点不可达,返回
            return;
        vis[k] = true;  //将k从Vb重除去,加入到Va中
        for(int j = 1; j <= N; ++j)
        {   //对于每个与k相邻的在Vb中的点j,更新s到j的最短距离
            if(!vis[j] && Map[k][j] != INF && Dist[j] > Dist[k] + Map[k][j])
            {
                Dist[j] = Dist[k] + Map[k][j];
                pre[j] = k;
            }
        }
    }
}

int main()
{
    int N,M,a,b,w;
    while(~scanf("%d%d",&N,&M) && (N||M))
    {   //初始化注意
        for(int i = 1; i <= N; ++i)
            for(int j = 1; j <= N; ++j)
                Map[i][j] = INF;
        //memet(Map,INF,sizeof(Map);这样是错的。不能这样子初始化。。。
        memset(Dist,INF,sizeof(Dist));
        memset(pre,0,sizeof(pre));
        for(int i = 0; i < M; ++i)
        {
            scanf("%d%d%d",&a,&b,&w);
            Map[a][b] = Map[b][a] = w;
        }
        Dijkstra(N,1);
        printf("%d\n",Dist[N]);
    }

    return 0;
}

BellmanFord算法:
处理带负权边的单元最短路径问题。
1.将Dist[]赋值为INF,出发点为s,Dist[s] = 0。
2.对于每条边(u,v),如果Dist[u] != INF,且Dist[v] > Dist[u] + Map[u][v],则Dist[v] = Dist[u] + Map[u][v]。
3.重复步骤(2) N-1次或直到某次中不再更新,进入步骤(4)。
4.对于每条边(u,v),如果Dist[u] != INF,且Dist[v] > Dist[u] + Map[u][v],则存在负权回路。
如果寻找负权回路,将Dist[]赋值为INF,寻找最短路径。
如果寻找正权回路,将Dist[]赋值为0,寻找最长路径。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 550;
const int MAXM = 5500;
const int INF = 0xffffff0;
struct EdgeNode
{
    int to;
    int w;
    int next;
}Edges[MAXM];
int Head[MAXN],Dist[MAXN];

bool BellmanFord(int N,int M)
{
    Dist[1] = 0;
    for(int i = 1; i < N; ++i)
    {
        for(int j = 1; j <= N; ++j)
        {
            if(Dist[j] == INF)
                continue;
            for(int k = Head[j]; k != -1; k = Edges[k].next)    //寻找最短路径
            {
                if(Edges[k].w != INF && Dist[Edges[k].to] > Dist[j] + Edges[k].w)
                    Dist[Edges[k].to] = Dist[j] + Edges[k].w;
            }
        }
    }

    for(int j = 1; j <= N; ++j)
    {
        if(Dist[j] == INF)
            continue;
        //Dist[u] != INF 且Dist[v] > Dist[u] + Map[u][v],则存在负权回路。寻找正权回路,则改变符号为<,去掉Dist[u] != INF
        for(int k = Head[j]; k != -1; k = Edges[k].next)
        {
            if(Edges[k].w != INF && Dist[Edges[k].to] > Dist[j] + Edges[k].w)
                return true;    //存在负权回路
        }
    }
    return false;   //不存在负权回路
}
int main()
{
    int F,N,M,W,S,E,T;
    scanf("%d",&F);
    while(F--)
    {
        //初始化
        memset(Dist,INF,sizeof(Dist));
        memset(Head,-1,sizeof(Head));
        memset(Edges,0,sizeof(Edges));
        scanf("%d%d%d",&N,&M,&W);
        int id = 0;
        for(int i = 0; i < M; ++i)
        {
            scanf("%d%d%d",&S,&E,&T);
            Edges[id].to = E;
            Edges[id].w = T;
            Edges[id].next = Head[S];
            Head[S] = id++;
            Edges[id].to = S;
            Edges[id].w = T;
            Edges[id].next = Head[E];
            Head[E] = id++;
        }
        for(int i = 0; i < W; ++i)
        {
            scanf("%d%d%d",&S,&E,&T);
            Edges[id].to = E;
            Edges[id].w = -T;
            Edges[id].next = Head[S];
            Head[S] = id++;
        }
        if(BellmanFord(N,M))
            printf("YES\n");
        else
            printf("NO\n");
    }

    return 0;
}

SPFA算法:
在计算带负边权图的单源最短路径基础上,降低时间复杂度。
1.初始化Dist[] = INF,Dist[s] = 0,新建一个队列,将源点S入队,标记S已经在队列中。
2.入队首取出一个点top,标记top已经出队,对top出队的次数进行检查,如果大于N,说明出现负环,算法结束。否则遍历top所连接的边,如果边k的另一端节点to的距离可以更新,即Dist[Edges[k].b] > Dist[top] + Edges[k].w,则更新Dist[Edges[k].b] = Dist[top] + Edges[k].w,检查b是否在队列中,如果不在,加入队列。
3.重复步骤(2),直到队列为空。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXM = 1000100;
const int MAXN = 1000100;

struct EdgeNode
{
    int to;
    int w;
    int next;
}Edges[2][MAXM];
int Head[2][MAXN],vis[MAXN],queue[MAXN],outque[MAXN];
__int64 Dist[MAXN];
//两个链式前向星,一个存正边,一个存反边(特殊题目要求,平常用一个),求1~N的最短路和N~1的最短路。
bool SPFA(int S,int N,int flag)
{
    for(int i = 2; i <= N; ++i)
        Dist[i] = 0xffffffff;
    memset(vis,0,sizeof(vis));
    memset(outque,0,sizeof(outque));
    int iq = 0;
    queue[iq++] = S;
    vis[S] = 1;
    Dist[S] = 0;
    int i = 0,top,k;
    while(i != iq)
    {
        top = queue[i];
        vis[top] = 0;
        outque[top]++;
        if(outque[top] > N) //如果出队次数大于N,则说明出现负环
            return false;
        k = Head[flag][top];    
        while(k >= 0)   //遍历top链接的边
        {   //如果边k的另一端点to的距离可以更新,则更新
            if(Dist[Edges[flag][k].to] - Edges[flag][k].w > Dist[top])
            {
                Dist[Edges[flag][k].to] = Dist[top] + Edges[flag][k].w;
                if( !vis[Edges[flag][k].to])    //检查to是否在队列中,不在则加入队列
                {
                    vis[Edges[flag][k].to] = 1;
                    queue[iq++] = Edges[flag][k].to;
                }
            }
            k = Edges[flag][k].next;
        }
        i++;
    }
    return true;
}

int main()
{
    int T,N,M,u,v,w;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&N,&M);
        memset(Head,-1,sizeof(Head));
        for(int i = 0; i < M; ++i)
        {
            scanf("%d%d%d", &u,&v,&w);
            Edges[0][i].to = v;
            Edges[0][i].w = w;
            Edges[0][i].next = Head[0][u];
            Head[0][u] = i;
            Edges[1][i].to = u;
            Edges[1][i].w = w;
            Edges[1][i].next = Head[1][v];
            Head[1][v] = i;
        }
        __int64 ans = 0;
        SPFA(1,N,0);
        for(int i = 1; i <= N; ++i)
            if(Dist[i] != 0xffffffff)
                ans += Dist[i];
        SPFA(1,N,1);
        for(int i = 1; i <= N; ++i)
            if(Dist[i] != 0xffffffff)
                ans += Dist[i];
        printf("%I64d\n",ans);

    }

    return 0;
}

单源最短路径Dijkstra、BellmanFord、SPFA【模板】

标签:

原文地址:http://blog.csdn.net/lianai911/article/details/45484707

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!