最短路模板详解合集

在这里插入图片描述
说明:n 为点数 m 为边数

单源最短路

所有边都是正数

dijkstra()算法(On^2)

Acwing 849 朴素版作法(适合稠密图 与边数无关)用邻接矩阵写
模板题从1 号点 到 n 号点最短路

#include<iostream>
#include<algorithm>
#include<cstring> //使用 memset 函数
using namespace std;
const int N=510;
int g[N][N],dist[N];
bool st[N];
int n,m;
int dijstra()
{
    memset(dist,0x3f,sizeof(dist));
    dist[1]=0;
    
    for(int i=0;i<n;i++) //其实就找了n-1次就可以 最后一个点不用更新也是最短的点
    {
        int t=-1;
        for(int j=1;j<=n;j++) //每次先选出当前点中最短的点
        {
            if(!st[j]&&(t==-1||dist[t]>dist[j]))
                t=j;
        }
        st[t]=true; //已经确定是最短点 
        for(int j=1;j<=n;j++) //用当前确定的点去更新其他点
            dist[j]=min(dist[j],dist[t]+g[t][j]);
    }
    //memset会把每个字节都写成0x3f,dist[n]是int型,包含4个字节,所以一定要写成0x3f3f3f3f
    if(dist[n]==0x3f3f3f3f) return -1;
    return dist[n];
}
int main()
{
    cin >>n >>m;
    memset(g,0x3f,sizeof(g));
    while(m--)
    {
       int a,b,c;
       cin >>a >>b >>c;
       g[a][b]=min(c,g[a][b]);
    }
    int t=dijstra();
    cout <<t<<endl;
    return 0;
}

堆优化的dijkstra() O(mlogn)

Acwing 850 适用于(稀疏图)O(mlogn)

#include<iostream>
#include<algorithm>
#include<cstring>
#define N 150005
#include<queue>
using namespace std;

int n,m;
int dist[N];
bool st[N];
int h[N],w[N],e[N],ne[N],idx; // 邻接表 的表示

void add(int a,int b,int c)
{   
    w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx,idx++;
}

typedef pair<int,int>PII;
//优先队列的使用方法 小根堆得到最小值定义名称为 heap
priority_queue< PII,vector<PII>,greater<PII> >heap;

int dijkstra()
{   //这里queue的使用方法套路一致
    memset(dist,0x3f,sizeof(dist));
    dist[1]=0;
    heap.push({0,1}); **//存储时first指的是路径距离second指的是结点**
    while(!heap.empty())
    {
        PII t = heap.top();   
        heap.pop();
        int a = t.second,b = t.first;  **//这里的a和朴素版的t 是一个作用**
        
        if(st[a]) continue; //每次都是集合中的最短路只用一次
        st[a]=true;
        
        for(int i=h[a];i!=-1;i=ne[i]) //邻接表的模板
        {    
            int j=e[i];//此时i代表的是当前节点的开始位置
                      //j代表的是该节点的结束位置,w[i]该节点的路径长度
            if(dist[j]>b+w[i])
            {
                dist[j]= b+w[i]; //这里的w[i]指的是朴素版的g[i][j]存的是两点的距离;
                heap.push({dist[j],j}); //还可能需要更新
            }
        }
    }
    //memset会把每个字节都写成0x3f,dist[n]是int型,包含4个字节,所以一定要写成0x3f3f3f3f
    if(dist[n]==0x3f3f3f3f) return -1;
    return dist[n];
}

int main()
{
    cin >>n >>m;
    memset(h,-1,sizeof(h));
    while(m--)
    {
        int x,y,z;
        cin >>x >>y >>z;
        add(x,y,z);
    }
    cout <<dijkstra()<<endl;
    return 0;
}

存在负权边

bellman-ford算法 O(nm)

有边数限制的最短路

用结构体来存数;
Acw 853.有边数限制的最短路;

#include<iostream>
#include<algorithm>
#include<cstring>
const int  N =505,M=10010;

using namespace std;
int n,m,k;
int dist[N],backup[N];

struct edge{
    int a,b,w;
}st[M]; 

int bellman_ford()
{
    memset(dist,0x3f,sizeof(dist));
    dist[1]=0;
    for(int i=0;i<k;i++) //进行限制性的K条边的操作
    {
        memcpy(backup,dist,sizeof(dist)); //有限制 要用上一轮的数组来比较,注意写法
        for(int j=0;j<m;j++)  //枚举的是每一条边
        {    //这里是m 个结构体每个结构体的 a,b,c;
            int a=st[j].a,b=st[j].b,w=st[j].w;
            dist[b]=min(dist[b],backup[a]+w);  //用上一轮的最小值更新因为这一轮可能会串联
        }
    }
   	//存在负环到不了正无穷 这里除2是最保险的;
    if(dist[n]>0x3f3f3f3f/2) return -1;
    return dist[n];
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=0;i<m;i++)
    {
        int a,b,w;
        scanf("%d%d%d",&a,&b,&w);
        st[i]={a,b,w};
    }
    int t=bellman_ford();
    if(t==-1) puts("impossible");
    else printf("%d\n",t);
    return 0;
}

spfa()算法 (一般O(m)最坏是O(nm))

Acwing 851 这里也是队列实现,不是最优队列
绝大多数 dijkstra()都能用spfa() 做 也是用邻接链表做

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N =2e5;//按边数来存
int n,m;
int dist[N];
bool st[N];

int h[N],w[N],e[N],ne[N],idx; 
void add(int a,int b,int c)
{
    w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx,idx++;
}
int spfa()
{
    memset(dist,0x3f,sizeof(dist));
    dist[1]=0;
    
    queue<int>q;
    q.push(1);
    st[1]=true; //当前这个点是不是在队列中 其实也不用写
    
    while(!q.empty())
    {
        int t=q.front();
        q.pop();
        st[t] = false;  //已经不在队列之中 
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i]; //用 t 这个点看是不是能当前这个点 
            if(dist[j]>dist[t]+w[i])
            {
                dist[j]=dist[t]+w[i];
                q.push(j);
                //没有这个判断好像也行
                if(!st[j]) //不在队列里   
                {  
                    q.push(j);
                    st[j]=true; //这里只是不会让他重复进队 重进的话顶多不更新
                }
            }
        }
    }
    if(dist[n]==0x3f3f3f3f) return -1;
    return dist[n];
}
int main()
{
    cin >>n>>m;
    memset(h,-1,sizeof(h));
    while(m--)
    {
        int x,y,z;
        cin >>x>>y >>z;
        add(x,y,z);
    }
    int t=spfa();
    if(t==-1) cout <<"impossible";
    else cout <<spfa();
    return 0;
}

用spfa()判断负环

用一个cnt[ ]数组来维护

#include<iostream>
#include<cstring>
#include<algorithm>
#define N 100005
#include<queue>

using namespace std;
int n,m;
int dist[N],cnt[N];
bool st[N];
int h[N],w[N],e[N],ne[N],idx; 
int add(int a,int b,int c)
{
    w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx,idx++;
}
int spfa()
{
    queue<int>q;
    for(int i=1;i<=n;i++)//所有点全都放那如入
    {
        st[i]=true;
        q.push(i);
    }
  
    while(!q.empty())
    {
        int t=q.front();
        q.pop();
        st[t] = false;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[t]+w[i])
            {
                cnt[j]=cnt[t]+1;
                if(cnt[j]>=n) return true;
                dist[j]=dist[t]+w[i];
                if(!st[t])
                {
                    q.push(j);
                    st[j]=true;
                }
            }
        }
    }
    return false;
}
int main()
{
    cin >>n>>m;
    memset(h,-1,sizeof(h));
    while(m--)
    {
        int x,y,z;
        cin >>x>>y >>z;
        add(x,y,z);
    }
    if(spfa()) puts("Yes");
    else puts("No");
    return 0;
}

多源汇最短路

Floyd算法(O(n^3))

题目
给定一个N个点m条变的有向图,可能存在重边和自环,边权可能为负数
再给定K个询问 输出两点之间的最短距离;
输入(此题为稠密图)
第一行n,m,k;
接下来m行 每行x,y,z; 表示点x到点y之间存在一条有向边边长为z;
接下来k行 每行a,b, 表示 询问 点a到点b之间的最短距离
输出
共K行每行输出一个整数表示询问的结果,若询问的两点之间不存在路径,输出 impossbile

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N =210,INF=1e9;

int n,m,q;
int d[N][N];
void floyd() //结束之后 d[i,j]存的就是最短距离
{
    for(int k=1;k<=n;k++) 
        for(int i=1;i<=n;i++) //从 i 这个点经过 1~k 这么多点到 j 的最短距离
            for(int j=1;j<=n;j++)
            d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}

int main()
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(i==j)d[i][j]=0;
            else d[i][j]=INF;
    while(m--)
    {
        int a,b,w;
        scanf("%d%d%d",&a,&b,&w);
        d[a][b]=min(d[a][b],w);
    }
    floyd();
    while(q--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(d[a][b]>INF/2)puts("impossbile");
        else printf("%d\n",d[a][b]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值