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

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;
}

时间: 2024-10-05 12:49:03

单源最短路径Dijkstra、BellmanFord、SPFA【模板】的相关文章

数据结构:单源最短路径--Dijkstra算法

Dijkstra算法 单源最短路径 给定一带权图,图中每条边的权值是非负的,代表着两顶点之间的距离.指定图中的一顶点为源点,找出源点到其它顶点的最短路径和其长度的问题,即是单源最短路径问题. Dijkstra算法 求解单源最短路径问题的常用方法是Dijkstra(迪杰斯特拉)算法.该算法使用的是贪心策略:每次都找出剩余顶点中与源点距离最近的一个顶点. 算法思想 带权图G=<V,E>,令S为已确定了最短路径顶点的集合,则可用V-S表示剩余未确定最短路径顶点的集合.假设V0是源点,则初始 S={V

单源最短路径Dijkstra算法

1.单源最短路径 函数:返回还未被收录顶点中dist最小者 1 Vertex FindMinDist(MGraph Graph, int dist[], int collected[]) 2 { 3 /*返回未被收录顶点中dist最小者*/ 4 Vertex MinV, V; 5 int MinDist = INFINITY; 6 7 8 for (V = 0; V < Graph->Nv; ++V) 9 { 10 if (collected[V] == false && di

单源最短路径 dijkstra算法实现

本文记录一下dijkstra算法的实现,图用邻接矩阵表示,假设图为无向图,并且连通,有向图,不连通图的做法类似. 算法简述: 首先确定"单源"的源,假设是第0个顶点. 维护三个数组dist[], color[], path[],设其下标分别为0-i-n-1: dist[] 表示源点到顶点i的最短距离,在初始化时,如果源点到顶点i有路径,则初始化为路径的权重,否则初始化为INT_MAX: color[] 数组其实表示两个集合,即color[i]值为1的集合表示已经确定最短路径的点的集合,

(转)图算法单源最短路径Dijkstra算法(邻接表/邻接矩阵+优先队列STL)

一.前言 最短路径算法,顾名思义就是求解某点到某点的最短的距离.消耗.费用等等,有各种各样的描述,在地图上看,可以说是图上一个地点到达另外一个地点的最短的距离.比方说,我们把地图上的每一个城市想象成一个点,从一个城市到另一个城市的花费是不一样的.现在我们要从上海去往北京,需要考虑的是找到一条路线,使得从上海到北京的花费最小.有人可能首先会想到,飞机直达啊,这当然是时间消耗最小的方法,但是考虑到费用的高昂,这条线路甚至还不如上海到北京的高铁可取.更有甚者,假设国家开通了从上海到西藏,再从西藏到兰州

hdu 2544 单源最短路问题 dijkstra+堆优化模板

最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 41168    Accepted Submission(s): 17992 Problem Description 在每年的校赛里.全部进入决赛的同学都会获得一件非常美丽的t-shirt.可是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的.所以如今他们想要寻

单源最短路径算法——Bellman-ford算法和Dijkstra算法

 BellMan-ford算法描述 1.初始化:将除源点外的所有顶点的最短距离估计值 dist[v] ← +∞, dist[s] ←0; 2.迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离:(运行|v|-1次) 3.检验负权回路:判断边集E中的每一条边的两个端点是否收敛.如果存在未收敛的顶点,则算法返回false,表明问题无解:否则算法返回true,并且从源点可达的顶点v的最短距离保存在 dist[v]中. 1 BELLMAN-FORD

单源最短路径——Dijkstra算法学习

每次都以为自己理解了Dijkstra这个算法,但是过没多久又忘记了,这应该是第4.5次重温这个算法了. 这次是看的胡鹏的<地理信息系统>,看完之后突然意识到用数学公式表示算法流程是如此的好理解,堪称完美. 内容摘抄如下: 网络中的最短路径是一条简单路径,即是一条不与自身相交的路径,最短路径搜索的依据:若从S点到T点有一条最短路径,则该路径上的任何点到S的距离都是最短的. Dijkstra算法搜索步骤: 1.对起始点作标记S,且对所有顶点令D(X)=∞,Y=S: 2.对所有未做标记的点按以下公式

单源最短路径Dijkstra和优先级算法

百度百科:迪杰斯特拉算法. 代码实现如下: import java.util.Comparator; import java.util.PriorityQueue; import java.util.Scanner; class Node { int x; int value; } public class Main { static int N,M,cnt; static final int MAX = 105; static final int INF = 1000009; static N

四大算法解决最短路径问题(Dijkstra+Bellman-ford+SPFA+Floyd)

什么是最短路径问题? 简单来讲,就是用于计算一个节点到其他所有节点的最短路径. 单源最短路算法:已知起点,求到达其他点的最短路径. 常用算法:Dijkstra算法.Bellman-ford算法.SPFA算法 多源最短路算法:求任意两点之间的最短路径. 常用算法:floyd算法 单源最短路径——Dijkstra Dijkstra算法是经典的最短路径算法,用于计算一个节点到其他所有节点的最短路径. 主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止. 时间复杂度:O(n^2) 处理问题:单源.