【Dijstra最短路】
【Dijstra】
【模版代码】
\(ver[x]\)是图
vector<i64> dis(n + 1, 1e18);
auto djikstra = [&](int s = 1) -> void {
using PII = pair<i64, int>;
priority_queue<PII, vector<PII>, greater<PII>> q;
q.emplace(0, s);
dis[s] = 0;
vector<int> vis(n + 1);
while (!q.empty()) {
int x = q.top().second;
q.pop();
if (vis[x]) continue;
vis[x] = 1;
for (auto [y, w] : ver[x]) {
if (dis[y] > dis[x] + w) {
dis[y] = dis[x] + w;
q.emplace(dis[y], y);
}
}
}
};
※【存图】
稠密图->邻接矩阵
稀疏图->邻接表
【模版题】
https://fjnuacm.top/d/minor/p/322?tid=66fbd9a7703d6adf52ed9b0c
【朴素Dijstra】
【思路】
集合st:当前已经确定最短距离的点
(1)初始化距离
dis[1]=0 dis[i]=很大的数
(2)for i : 1-n
t <- 不在s中的 距离最近的点
st <- t
用t更新其他点的距离:
t ○ --> x ○ dis[x]>dis[t]+w
【举例】
【代码】
//朴素Dijstra求最短路:稠密图
/*【思路】
集合st:当前已经确定最短距离的点
(1)初始化距离
dis[1]=0 dis[i]=很大的数
(2)for i : 1-n
t <- 不在s中的 距离最近的点
st <- t
用t更新其他点的距离:
t ○ --> x ○ dis[x]>dis[t]+w
*/
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int n,m;
int g[N][N];//邻接矩阵
int dist[N];
bool st[N];
int dijstra(){
//初始化
memset(dist,0x3f,sizeof dist);
dist[1]=0;
//迭代n次
for(int i=0;i<n;i++){
int t=-1;
//找t: 不在s中的 距离最近的点
for(int j=1;j<=n;j++){
if(!st[j] && (t==-1 || dist[t]>dist[j])){
t=j;
}
}
if(t==n) break;//已经找到最短距离了
//更新st
st[t]=true;//走过这个点
//用t更新其他点距离
for(int j=1;j<=n;j++){
dist[j]=min(dist[j],g[t][j]+dist[t]);
}
}
if(dist[n]==0x3f3f3f3f) return -1;//不连通
return dist[n];
}
int main(){
scanf("%d%d",&n,&m);
memset(g,0x3f,sizeof g);//初始化最大值
while(m--){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
//处理重边:因为要求最短距离->留最短值
g[x][y]=min(g[x][y],z);
}
//处理自环:g[i][i]=0
for(int i=0;i<=N;i++){
g[i][i]=0;
}
int ans=dijstra();
printf("%d",ans);
return 0;
}
【堆优化Dijstra】
【优化思路】
优先队列模拟堆->模拟找最小值t的过程
->更新时将每个点都存进去:O(mlogm)
【代码】
//堆优化Dijstra求最短路:稀疏图
/*【思路】
集合st:当前已经确定最短距离的点
(1)初始化距离
dis[1]=0 dis[i]=很大的数
(2)for i : 1-n
t <- 不在s中的 距离最近的点
st <- t
用t更新其他点的距离:
t ○ --> x ○ dis[x]>dis[t]+w
*/
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=150010;
int n,m;
int h[N],ne[N],e[N],idx,val[N];//邻接表存图
int dist[N];
bool st[N];
void add(int x,int y,int c){
e[idx]=y;
ne[idx]=h[x];
h[x]=idx;
val[idx++]=c;
}
int dijstra(){
//初始化
memset(dist,0x3f,sizeof dist);
dist[1]=0;
priority_queue<PII,vector<PII>,greater<PII>> heap;//优先队列存小根堆
heap.push({0,1});//first:距离 second:节点编号
while(heap.size()){
PII t=heap.top();
heap.pop();
int ver=t.second;
int distance=t.first;
if(st[ver]) continue;
st[ver]=true;
for(int i=h[ver];i!=-1;i=ne[i]){
int j=e[i];
if(dist[j]>distance+val[i]){
dist[j]=distance+val[i];//注意这里val是根据idx的计数存的
heap.push({dist[j],j});
}
}
}
if(dist[n]==0x3f3f3f3f) return -1;//不连通
return dist[n];
}
int main(){
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);//初始化最大值
while(m--){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
//自环和重边都不需要处理->找最短路的性质决定了
}
int ans=dijstra();
printf("%d",ans);
return 0;
}
题目积累
紧急救援
https://pintia.cn/problem-sets/994805046380707840/exam/problems/type/7?problemSetProblemId=994805073643683840&page=1
带多个附加量的最短路:在Dij遍历每条边的时候跟着转移即可
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=510;
//带多个关键字的最短路
//Dij可以遍历每一条边->其他需要转移的量跟着遍历的时候变就可以
int n,m,s,d;
int a[N];
int h[N],ne[N*N],e[N*N],val[N*N],idx;
void add(int x,int y,int v){
e[idx]=y;
ne[idx]=h[x];
h[x]=idx;
val[idx++]=v;
}
//dij用
int dis[N];
bool vis[N];
//救援队数量
int st[N];
//路径条数
int road[N];
//父亲方法回溯
int father[N];
void dijstra(int s,int d){
memset(dis,0x3f,sizeof dis);
dis[s]=0;
road[s]=1;//起始路径设为1
priority_queue<PII,vector<PII>,greater<PII>> heap;
heap.push({0,s});
while(heap.size()){
PII p=heap.top();
heap.pop();
int distance=p.first;
int ver=p.second;
if(vis[ver]) continue;
vis[ver]=1;
for(int k=h[ver];k!=-1;k=ne[k]){
int j=e[k];
//注意要特判路径相同的情况
if(dis[j]>val[k]+distance){
father[j]=ver;
st[j]=st[ver]+a[j];
road[j]=road[ver];
dis[j]=val[k]+distance;
heap.push({dis[j],j});
}
else if(dis[j]==val[k]+distance){
if(st[j]<st[ver]+a[j]){
father[j]=ver;
st[j]=st[ver]+a[j];
}
road[j]+=road[ver];//路径数相加记得放外面
}
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>s>>d;
//注意下标从0开始!
for(int i=0;i<n;i++) cin>>a[i];
memset(h,-1,sizeof h);
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
//路径条数/节点和初始化
for(int i=0;i<n;i++){
father[i]=i;
st[i]=a[i];
}
dijstra(s,d);
cout<<road[d]<<" "<<st[d]<<endl;
vector<int> ans;
int p=d;
while(1){
ans.push_back(p);
p=father[p];
if(p==s){
ans.push_back(s);
break;
}
}
reverse(ans.begin(),ans.end());
for(int i=0;i<ans.size()-1;i++){
cout<<ans[i]<<" ";
}
cout<<ans[ans.size()-1];
return 0;
}