单源最短路(dijkstra、spfa及其优化)

题目链接

P4779 【模板】单源最短路径(标准版)

P3371 【模板】单源最短路径(弱化版)


P4779 【模板】单源最短路径(标准版)

题目描述

给定一个 \(n\) 个点,\(m\) 条有向边的带非负权图,请你计算从 \(s\) 出发,到每个点的距离。

数据保证你能从 \(s\) 出发到任意点。

输入格式

第一行为三个正整数 \(n, m, s\)。 第二行起 \(m\) 行,每行三个非负整数 \(u_i, v_i, w_i\) ,表示从 \(u_i\)\(v_i\) 有一条权值为 \(w_i\) 的有向边。

输出格式

输出一行 \(n\) 个空格分隔的非负整数,表示 \(s\) 到每个点的距离。

输入

4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4

输出

0 2 4 3

说明/提示

\(1 \leq n \leq 10^5\)

\(1 \leq m \leq 2\times 10^5\)

\(s = 1\)

\(1 \leq u_i, v_i\leq n\)

\(0 \leq w_i \leq 10 ^ 9 , 0 \leq \sum w_i \leq 10 ^ 9\)

dijkstra

  • 时间复杂度:\(O(mlogn)\)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef pair<int,int> pii;
int n,m,s;
int d[N];
vector<pii> adj[N];
priority_queue<pii,vector<pii>,greater<pii>> q;
void dijkstra(int s)
{
    memset(d,0x3f,sizeof d);
    d[s]=0;
    q.emplace(0,s);
    while(q.size())
    {
        auto [t,x]=q.top();
        q.pop();
        if(t>d[x])continue;
        for(auto [y,w]:adj[x])
        {
            if(d[y]>d[x]+w)
            {
                d[y]=d[x]+w;
                q.emplace(d[y],y);
            }
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    while(m--)
    {
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        adj[x].emplace_back(y,w);//注意是有向边
    }
    dijkstra(s);
    for(int i=1;i<=n;i++)
        printf("%d ",d[i]);
    return 0;
}

P3371 【模板】单源最短路径(弱化版)

题面同上,不过上题使用spfa会卡~

spfa

  • 时间复杂度:\((km)\)
  • 最坏时间复杂度:\((nm)\)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,s;
int d[N];
bool v[N];
vector<pair<int,int>> adj[N];
queue<int> q;
void spfa(int s)
{
    memset(d,0x3f,sizeof d);
    d[s]=0;
    v[s]=true;
    q.push(s);
    while(q.size())
    {
        int x=q.front();
        q.pop();
        v[x]=false;
        for(auto [y,w]:adj[x])
        {
            if(d[y]>d[x]+w)
            {
                d[y]=d[x]+w;
                if(!v[y])v[y]=true,q.push(y);
            }
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    while(m--)
    {
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        adj[x].emplace_back(y,w);//注意是有向边
    }
    spfa(s);
    for(int i=1;i<=n;i++)
        if(i==s)printf("0 ");
        else
            printf("%d ",d[i]==0x3f3f3f3f?INT_MAX:d[i]);
    return 0;
}
  • SLF(入队顶点)\(\rightarrow\) 如果说当前点所花费的值少于当前队头点的值的话,那么我们就将这个节点插入到队头去,否则还是插入到队尾.
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n,m,s;
vector<pair<int,int>> adj[N];
bool v[N];
int d[N];

void spfa(int s)
{
    deque<int> q;
    q.push_back(s);
    v[s]=true;
    memset(d,0x3f,sizeof d);
    d[s]=0;
    while(q.size())
    {
        int x=q.front();
        q.pop_front();
        v[x]=false;
        for(auto [y,w]:adj[x])
        {
            if(d[y]>d[x]+w)
            {
                d[y]=d[x]+w;
                if(q.size()&&d[y]<d[q.front()])q.push_front(y);
                else
                    q.push_back(y);
                v[y]=true;
            }
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    while(m--)
    {
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        adj[x].emplace_back(y,w);
    }
    spfa(s);
    for(int i=1;i<=n;i++)
        printf("%d ",d[i]==0x3f3f3f3f?INT_MAX:d[i]);
    return 0;
}

  • LLL(出队顶点)\(\rightarrow\)\(dist[ q.front] *num>sum\) ,则将 \(q.front\) 取出插入到队尾,查找下一元素,直到找到某一个 \(q.front\) 使得 \(dis[ q.front ]*num <= sum\) ,则将 \(q.front\) 出队进行松弛操作。
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n,m,s;
vector<pair<int,int>> adj[N];
bool v[N];
int d[N];
int sum,num;
void spfa(int s)
{
    deque<int> q;
    q.push_back(s);
    v[s]=true;
    memset(d,0x3f,sizeof d);
    d[s]=0;
    num=1;
    while(q.size())
    {
        int x=q.front();
        while(num*d[x]>sum)
        {
            q.pop_front();
            q.push_back(x);
            x=q.front();
        }
        q.pop_front();
        sum-=d[x];
        num--;
        v[x]=false;
        for(auto [y,w]:adj[x])
        {
            if(d[y]>d[x]+w)
            {
                d[y]=d[x]+w;
                q.push_back(y);
                sum+=d[y];
                num++;
                v[y]=true;
            }
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    while(m--)
    {
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        adj[x].emplace_back(y,w);
    }
    spfa(s);
    for(int i=1;i<=n;i++)
        printf("%d ",d[i]==0x3f3f3f3f?INT_MAX:d[i]);
    return 0;
}

  • SLF+LLL
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n,m,s;
vector<pair<int,int>> adj[N];
bool v[N];
int d[N];
int sum,num;
void spfa(int s)
{
    deque<int> q;
    q.push_back(s);
    v[s]=true;
    memset(d,0x3f,sizeof d);
    d[s]=0;
    num=1;
    while(q.size())
    {
        int x=q.front();
        while(num*d[x]>sum)
        {
            q.pop_front();
            q.push_back(x);
            x=q.front();
        }
        q.pop_front();
        sum-=d[x];
        num--;
        v[x]=false;
        for(auto [y,w]:adj[x])
        {
            if(d[y]>d[x]+w)
            {
                d[y]=d[x]+w;
                if(q.size()&&d[y]<d[q.front()])q.push_front(y);
                else
                    q.push_back(y);
                sum+=d[y];
                num++;
                v[y]=true;
            }
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    while(m--)
    {
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        adj[x].emplace_back(y,w);
    }
    spfa(s);
    for(int i=1;i<=n;i++)
        printf("%d ",d[i]==0x3f3f3f3f?INT_MAX:d[i]);
    return 0;
}
  • dfs优化(一般用于负环)
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n,m,s;
vector<pair<int,int>> adj[N];
bool v[N],dfs[N];
int d[N];
bool flag;
void spfa_dfs(int x)
{
    v[x]=true;
    for(auto [y,w]:adj[x])
    {
        if(d[y]>d[x]+w)
        {
            if(v[y])
            {
                flag=true;
                return ;
            }
            d[y]=d[x]+w;
            spfa_dfs(y);
        }
    }
    v[x]=false;
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    while(m--)
    {
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        adj[x].emplace_back(y,w);
    }
    memset(d,0x3f,sizeof d);
    d[s]=0;
    spfa_dfs(s);
    for(int i=1;i<=n;i++)
        printf("%d ",d[i]==0x3f3f3f3f?INT_MAX:d[i]);
    return 0;
}
posted @ 2021-09-22 11:19  zyy2001  阅读(69)  评论(0编辑  收藏  举报