图论-模板-SPFA与迪杰斯特拉算法求单源最短路

SPFA与迪杰斯特拉算法求单源最短路

这里的SPFA是没有任何优化的SPFA。
关于SPFA的dfs使用本人正在学习,包括更深层次的原理理解以及和dfs判断负环,比较,,O(VE)的bfs还是太慢了。

比较

SPFA极其不稳定,正权图上最坏时间复杂度为O(VE),负权图可达指数级。SPFA已死!!!!,不过判断负环还是蛮有用的。

SPFA Dijkstra
时间复杂度 正权图O(VE),负权图指数级 只能求正权图,O(ElogE)

对于存在负环的图,也就是存在一回路的权值和为负数,不存在最短路,不纳入考虑范围。

Dijkstra

大致步骤:
从源点开始bfs。每次搜索都是从当前已打通的节点中拓展一个目前能直接到达的节点,且源点到该节点的路径上的权值和,是在目前能到达的节点中最小,那么源点到该节点的最短路劲就是此权值和。弹出后再加入新的边就好。

比如说,bfs前加入源点。bfs进行第一次搜索时,把源点能够直接到达的点全部加入优先队列,然后弹出队首权值最小节点,这时候我们获得了该点的最短路,然后再把该节点连通的节点计算路径长度后加入优先队列。

对于重复遇见的节点,大胆加入,如果不是最优,到时候根据vis数组直接continue就好。

证明:每次队列弹出的首个节点U的最短路就是目前所求得的最小权值和sumw(应为可能反复遇见)
假设不是,那么因该存在其它路径比sumw更小,因为sumw已经是最小的了,而其它其他路径就必须经由其它其它节点到达U,那么这个路劲长度肯定大于sumw,所以为sumw。得证。
(乱证捂脸,不保证正确)
代码:

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
typedef long long ll;
typedef unsigned long long ull;
const ll MAXN=1e18;
const int MOD=1e6;

struct linkstar{
    int to,pre,w;
}edge[200050];
int head[100050],cnt=0;
int dis[100050];

void add(int u,int v,int w){
    edge[++cnt].to=v;
    edge[cnt].pre=head[u];
    edge[cnt].w=w;
    head[u]=cnt;
}
//链式前向星存图
struct node{
    int u,dis;
};
struct cmp{
    bool operator()(node &x,node &y){
        return x.dis>y.dis;
    }
};
//优先队列预备
bool vis[100050];
void dij(int s){//dij
    priority_queue<node,vector<node>,cmp> que;
    que.push({s,0});
    while(que.size()){
        int u=que.top().u;
        que.pop();
        if(vis[u]==1) continue;//已获得的最短路就不要这个了
        vis[u]=1;
        for(int v=head[u];v!=0;v=edge[v].pre){
            if(vis[edge[v].to]==1)  continue;//已获得了最短路就不要这个了
            int len=dis[u]+edge[v].w;
            if(len<dis[edge[v].to]){
                dis[edge[v].to]=len;
                que.push({edge[v].to,len});
            }
        }
    }
    return;
}
int main(){
    IOS;
    int n,m,s,u,v,w;
    cin>>n>>m>>s;
    for(int i=1;i<=n;++i)  dis[i]=2e9;
    dis[s]=0;
    for(int i=1;i<=m;++i){
        cin>>u>>v>>w;
        add(u,v,w);
    }
    dij(s);
    for(int i=1;i<=n;++i) {
        cout<<dis[i]<<' ';
    }
    return 0;
}

板子题https://www.luogu.com.cn/problem/P4779

SPFA

目前还不太理解原理,,,到时候整合后放在判断负环里。治理直接放代码

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
typedef long long ll;
typedef unsigned long long ull;
const ll MAXN=1e18;
const int MOD=1e6;

struct node{
    int to,pre,w;
}link[500050];
int head[100050],cnt=0;
ll dis[10005];

void add(int u,int v,int w){
    link[++cnt].to=v;
    link[cnt].pre=head[u];
    link[cnt].w=w;
    head[u]=cnt;
}

bool vis[10005];
void spfa(int s){
    queue<int> que;
    que.push(s);
    vis[s]=1;
    while(que.size()){
        int u=que.front();
        que.pop();
        vis[u]=0;
        int v=head[u];
        while(v){
            int len=dis[u]+link[v].w;
            if(len<dis[link[v].to]){
                dis[link[v].to]=len;
                if(vis[link[v].to]==0){
                    vis[link[v].to]=1;
                    que.push(link[v].to); 
                } 
            }
            v=link[v].pre;
        }
    }
    return ;
}

int main(){
    IOS;
    int n,m,s,u,v,w;
    cin>>n>>m>>s;
    dis[s]=0;
    for(int i=1;i<=n;++i){
        if(i!=s) dis[i]=2e18;
    }
    for(int i=1;i<=m;++i){
        cin>>u>>v>>w;
        add(u,v,w);
    }
    spfa(s);
    for(int i=1;i<=n;++i) {
        if(dis[i]==2e18) cout<<(long long)(1<<31)-1<<' ';
        else cout<<dis[i]<<' ';
    }
    return 0;
}
posted @ 2021-05-11 14:57  七铭的魔法师  阅读(80)  评论(0编辑  收藏  举报