[模板]最短路 (Floyd, Spfa, Dijkstra,

最短路总结

//稀疏图选用邻接表来存储要比邻接矩阵来存储要好很多


根据HDU 1874裸最短路,写了各种最短路模板


hdu 1874 畅通工程续(无向图

//Floyd

Floyd算法的学习可以点进这个链接,点这里,非常简单的算法,链接里也介绍的非常详尽^_^

//Floyd算法
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <string.h>
#include <queue>
#define N 200+5
#define inf 1<<29
using namespace std;


int e[N][N];


void inti(int n, int m)
{
    int i, j, a, b, c;
    for(i = 0; i < n; i++)
    {
        for(j = 0; j < n; j++)
            e[i][j] = inf;
        e[i][i] = 0;
    }
    while(m-->0)
    {
        scanf("%d%d%d", &a, &b, &c);
        if(c < e[a][b])
            e[a][b] = e[b][a] = c;
    }
}
void floyd(int n)
{
    int i, j, k;
    for(k = 0; k < n; k++)
        for(i = 0; i < n; i++)
            for(j = 0; j < n; j++)
                if(e[i][j] > e[i][k] + e[k][j])
                    e[i][j] = e[i][k] + e[k][j];
}
int main()
{
    int n, m;
    int a, b;


    while(~scanf("%d%d", &n, &m))
    {
        inti(n,m);
        floyd(n);
        scanf("%d%d", &a, &b);
        if(e[a][b] != inf)
            printf("%d\n", e[a][b]);
        else
            printf("-1\n");
    }
    return 0;
}

//spfa

spfa的邻接表数组实现,学习链接点这里,通俗易懂,看完这个看下面写的邻接表的队列实现的储存数据那更好理解

//spfa+邻接表的数组实现
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <string.h>
#include <queue>
#define N 201
#define M 201
#define inf 1<<29
using namespace std;


int dis[N];
int u[M],v[M], w[M];
int p[N];//记录每个顶点其中一条边的编号
int next[M];//记录编号为i的边的前一条边的编号
int e;


void input(int m)
{
    for(int i = 0; i < m; i++)
    {
        scanf("%d%d%d", &u[i], &v[i], &w[i]);
        next[i] = p[u[i]];
        p[u[i]] = i;
    }
}
void spfa(int s)
{
    int k = p[s];
    while(k != -1)
    {
        if(dis[v[k]] > dis[s] + w[k])
        {
            dis[v[k]] = dis[s] + w[k];
            if(v[k] != e)
                spfa(v[k]);
        }
        k = next[k];
    }
}
int main()
{
    int n, m;
    int s;


    while(~scanf("%d%d", &n, &m))
    {
        memset(p,-1,sizeof(p));
        fill(dis,dis+N,inf);
        input(m);
        scanf("%d%d", &s, &e);
        dis[s] = 0;
        spfa(s);
        if(dis[e] != inf)
            printf("%d\n", dis[e]);
        else
            printf("-1\n");
    }


    return 0;
}

//但是没过,说是complication error,编译错误,不是很懂......求大神点醒


无向图的邻接表、有向图的正逆邻接表这个博客写的不错,很清楚,实现过程也清楚,可以点这里看看

spfa中我最喜欢这个实现,如果看不懂怎么存储数据使用的,可以自己推演一遍,如果不懂就再推演,学会了会很方便^_^

//spfa+邻接表的队列实现
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <string.h>
#include <queue>
#define N 300
#define inf 1<<29
using namespace std;


struct node{
    int v;
    int c;
    int next;//记录编号为i的边的前一条边的编号
}edge[N*2];
int dis[N];
int path[N];//记录每个顶点其中一条边的编号
int vis[N];//是否在队列中


void add(int m)
{
    int i = 0;
    int a, b, c;
    while(m--)
    {
        scanf("%d%d%d", &a, &b, &c);
        edge[i].v = b;
        edge[i].c = c;
        edge[i].next = path[a];
        path[a] = i++;
        edge[i].v = a;
        edge[i].c = c;
        edge[i].next = path[b];
        path[b] = i++;
    }
}
void spfa(int s)
{
    dis[s] = 0;
    queue<int>Q;
    Q.push(s);
    while(!Q.empty())
    {
        int t = Q.front();
        Q.pop();
        vis[t] = false;
        for(int i = path[t]; i != -1; i = edge[i].next)
        {
            int w = edge[i].c;
            int temp  = edge[i].v;
            if(dis[temp] > dis[t] + w)
            {
                dis[temp] = dis[t] + w;
                if(!vis[temp])
                {
                    Q.push(temp);
                    vis[temp] = true;
                }
            }
        }
    }


}
int main()
{
    int n, m;
    int s, e;


    while(~scanf("%d%d", &n, &m))
    {
        memset(path, -1, sizeof(path));
        memset(vis, false, sizeof(vis));
        fill(dis, dis+N, inf);
        add(m);
        scanf("%d%d", &s, &e);
        spfa(s);
        if(dis[e] != inf)
            printf("%d\n", dis[e]);
        else
            printf("-1\n");
    }


    return 0;
}


邻接矩阵的实现可以点这里学习

//spfa+邻接表的邻接矩阵实现
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <string.h>
#include <queue>
#define N 205
#define inf 1<<29
using namespace std;


int map[N][N];
int dis[N];
int vis[N];//是否在队列中


void inti(int n, int m)
{
    int i, j;
    int a, b, c;
    memset(vis, false, sizeof(vis));
    fill(dis, dis+N, inf);
    for(i = 0; i < n; i++)
    {
        for(j = 0; j < n; j++)
            map[i][j] = inf;
        map[i][i] = 0;
    }
    while(m--)
    {
        scanf("%d%d%d", &a, &b, &c);
        if(c < map[a][b])
            map[a][b] = map[b][a] = c;
    }
}
void spfa(int n, int s)
{
    dis[s] = 0;
    queue<int>Q;
    Q.push(s);
    while(!Q.empty())
    {
        int t = Q.front();
        Q.pop();
        vis[t] = false;
        for(int i = 0; i < n; i++)
        {
            if(dis[i] > dis[t] + map[t][i] && map[t][i]!=inf)
            {
                dis[i] = dis[t] + map[t][i];
                if(!vis[i])
                {
                    Q.push(i);
                    vis[i] = true;
                }
            }
        }
    }
}
int main()
{
    int n, m;
    int s, e;


    while(~scanf("%d%d", &n, &m))
    {
        inti(n, m);
        scanf("%d%d", &s, &e);
        spfa(n, s);
        if(dis[e] != inf)
            printf("%d\n", dis[e]);
        else
            printf("-1\n");
    }


    return 0;
}


//dijkstra

dijkstra的算法基本思想:每次找到离源点最近的一个顶点,以此顶点为中心扩展,更新源点到其余所有点的最短路径。具体可以看这个

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string.h>
#include <cmath>
#include <string>
#define N 205
#define inf 1<<29
using namespace std;
//单源最短路
bool vis[N];
int map[N][N];
int dis[N];
int s, e;

void inti(int n, int m)
{
    int i, j;
    int a, b, c;
    memset(vis, false, sizeof(vis));
    fill(dis, dis+N, inf);
    for(i = 0; i < n; i++)
    {
        for(j = 0; j < n; j++)
            map[i][j] = inf;
        map[i][i] = 0;
    }
    while(m-->0)
    {
        scanf("%d%d%d", &a, &b, &c);
        if(map[a][b] > c)//两点之间不止一条路径
            map[a][b] = map[b][a] = c;//无向图
    }
    scanf("%d%d", &s, &e);
    ///记录源点s到其余点的初始路径
    for(i = 0; i < n; i++)
        dis[i] = map[s][i];
    vis[s] = true;
}
void dijkstra(int n)
{
    int i, j, k;
    int min;
    int t = s;//注意赋初值

    for(i = 0; i < n-1; i++)
    {
    //找出距离i点的最短路径点
        min = inf;
        for(j = 0; j < n; j++)
            if(!vis[j] && dis[j] < min)
            {
                min = dis[j];
                t = j;
            }
        vis[t] = true;
        for(k = 0; k < n; k++)
            if(dis[k] > dis[t] + map[t][k])
                dis[k] = dis[t] + map[t][k];
    }
}

int main()
{
    int n, m;

    while(~scanf("%d%d", &n, &m))
    {
        inti(n, m);
        dijkstra(n);
        if(dis[e] != inf)
            printf("%d\n", dis[e]);
        else
            printf("-1\n");
    }
    return 0;
}

//待补充

//spfa+邻接表的vector模拟


补充:

关于Floyd、Dijkstra、Bellman-Ford、Spfa算法的比较,可以先看这个博客,总结的不错

简而言之:

Floyd:多源、无负权边(原理是动态规划,矩阵存,时效性差

Dijkstra:单源、无负权边(堆的运用还没怎么看...

Bellman-Ford:单源、可有负权边、无负环

Spfa:单源、可有负权边、无负环(邻接表存,Bellman的队列优化,可完全替代Bellman)


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值