单源最短路(dijkstra、spfa及其优化)
题目链接
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;
}