图论之最短路算法模板总结

来个大致的分类:

朴素的迪杰斯特拉:

实现:

我们让s表示当前已经确定的最短距离的点,我们找到一个不在s中的距离最近的点t,并用t来更新其他的点。

下面是AC代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
//求1---n的最短路(用邻接矩阵存储)
const int N=510;
int n,m;
int g[N][N];
int dis[N];
bool st[N];
int dij()
{
    memset(dis,0x3f,sizeof(dis));
    dis[1]=0;
    for(int i=0;i<n;i++)//一共n次
    {
        int t=-1;
        //找最近的点
        for(int j=1;j<=n;j++)
        {
            if(!st[j]&&(t==-1||dis[t]>dis[j])) t=j;
        }
        st[t]=1;
        //更新
        for(int j=1;j<=n;j++)
        {
            dis[j]=min(dis[j],dis[t]+g[t][j]);
        }
        
    }
    if(dis[n]==1e8) return -1;
    return dis[n];
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i==j) g[i][j]=0;
            else g[i][j]=1e8;
        }
    }
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        g[a][b]=min(g[a][b],c);
    }
    cout<<dij();
}

堆优化的迪杰斯特拉:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=100100;
int n,m;
int h[N],w[N],e[N],ne[N],id;
int dis[N];
bool st[N];
typedef pair<int,int> pii;
void add(int a,int b,int c)
{
    e[id]=b,w[id]=c,ne[id]=h[a],h[a]=id++;
}
int dij()
{
    memset(dis,0x3f,sizeof(dis));
    dis[1]=0;
    priority_queue<pii,vector<pii>,greater<pii>> heap;
    heap.push({0,1});
    
    while(!heap.empty())
    {
        auto t=heap.top();
        heap.pop();
        int ver=t.second,dist=t.first;
        if(st[ver]) continue;
        st[ver]=1;
        for(int i=h[ver];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dis[j]>dist+w[i])
            {
                dis[j]=dist+w[i];
                heap.push({dis[j],j});
            }
        }
    }
    if(dis[n]==0x3f3f3f3f) return -1;
    return dis[n];
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof(h));
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    cout<<dij();
}

Bellman-Ford算法:

实现:循环n次,每一次循环所有边并更新。

注意:有负权的可能没有最短路,我们最外面循环了k次,他的含义是最多经过k条边的最短路,因此若循环超过该次数说明有环,这样子就是有负环了,但是有负环不一定说明答案不存在比方说一个与答案路径无关的负环,因此一般无法用该算法判断。

下面是AC代码(只可以走k条边):

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=510,M=100010;
int n,m,k,f;
int dis[N],backup[N];
struct node
{
    int a,b,w;
    
}edge[M];
int bel()
{
    memset(dis,0x3f,sizeof(dis));
    dis[1]=0;
    for(int i=0;i<k;i++)
    {
        memcpy(backup,dis,sizeof(dis));//防止更新时用同一回的更新
        for(int j=0;j<m;j++)
        {
            int a=edge[j].a,b=edge[j].b,w=edge[j].w;
            dis[b]=min(dis[b],backup[a]+w);
        }
    }
    if(dis[n]>0x3f3f3f3f/2){
        f=1;
        return -1;
    } 
    //有负权边
    return dis[n];
}
int main()
{
    cin>>n>>m>>k;
    for(int i=0;i<m;i++)
    {
        int a,b,w;
        scanf("%d%d%d",&a,&b,&w);
        edge[i]={a,b,w};
    }
    int t=bel();
    if(t==-1&&f) cout<<"impossible";
    else cout<<t;
}

SPFA算法:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=100100;
int n,m;
int h[N],w[N],e[N],ne[N],id;
int dis[N];
bool st[N];
void add(int a,int b,int c)
{
    e[id]=b,w[id]=c,ne[id]=h[a],h[a]=id++;
}
int spfa()
{   memset(dis,0x3f,sizeof(dis));
    dis[1]=0;
    queue<int> q;
    q.push(1);
    st[1]=1;
    while(q.size())
    {
        int t=q.front();
        q.pop();
        st[t]=0;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dis[j]>dis[t]+w[i])
            {
                dis[j]=dis[t]+w[i];
                if(!st[j])
                {
                    q.push(j);
                    st[j]=1;
                }
            }
        }
    }
    if(dis[n]==0x3f3f3f3f) return -1;
    return dis[n];
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof(h));
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    int t=spfa();
    if(t==-1) cout<<"impossible";
    else cout<<t;
}

SPFA求负环:

同时维护cntx表示当前最短路的边数,我们cntx=cntt+1,若有cntx>=n,说明有了负环。

下面是AC代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=100100;
int n,m;
int h[N],w[N],e[N],ne[N],id;
int dis[N],cnt[N];
bool st[N];
void add(int a,int b,int c)
{
    e[id]=b,w[id]=c,ne[id]=h[a],h[a]=id++;
}
int spfa()
{   
    queue<int> q;
    for(int i=1;i<=n;i++)
    {
        st[i]=1;
        q.push(i);
    }//相当于一个超级源点
    while(q.size())
    {
        int t=q.front();
        q.pop();
        st[t]=0;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dis[j]>dis[t]+w[i])
            {
                dis[j]=dis[t]+w[i];
                cnt[j]=cnt[t]+1;
                if(cnt[j]>=n) return 1;
                if(!st[j])
                {
                    q.push(j);
                    st[j]=1;
                }
            }
        }
    }
    return 0;
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof(h));
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    int t=spfa();
    if(t==1) cout<<"Yes";
    else cout<<"No";
}

Floyd算法:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N = 210, INF = 1e9;
int n, m, q;
int d[N][N];
void floyd()
{
    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            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 <= n; j ++ )
            if (i == j) d[i][j] = 0;
            else d[i][j] = INF;
    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        d[a][b] = min(d[a][b], c);
    }

    floyd();
    while (q -- )
    {
        int a, b;
        scanf("%d%d", &a, &b);

        int t = d[a][b];
        if (t > INF / 2) puts("impossible");
        else printf("%d\n", t);
    }
}
相关推荐
羊羊小栈1 小时前
Uplift营销供应链协同决策系统(基于Uplift因果推断与运筹优化算法)
前端·人工智能·算法·毕业设计·大作业
金融小师妹1 小时前
AI因子共振模型显示:金银比突破区间上沿,白银定价逻辑进入再校准阶段
人工智能·算法·均值算法·线性回归
J2虾虾1 小时前
C语言 typedef 用法
c语言·数据结构·算法
hunterkkk(c++)2 小时前
线段树例题
算法
故渊at2 小时前
第二板块:Android 四大组件标准化学理 | 第七篇:Activity 页面载体与任务栈算法
android·算法·生命周期·activity·任务栈
兰令水2 小时前
leecodecode【区间DP+树形DP】【2026.6.10打卡-java版本】
java·算法·leetcode
weixin199701080162 小时前
[特殊字符] 1688开放平台API Sign签名算法详解(Java / Python / PHP 实现)
java·python·算法
未若君雅裁2 小时前
JVM 垃圾回收算法与分代回收机制
java·jvm·算法
智者知已应修善业3 小时前
【51单片机初始化D5-D8亮,每按键按下D1到D4全亮,再按下恢复,如此循环】2024-3-26
c++·经验分享·笔记·算法·51单片机