【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

【举例】
image

【代码】

//朴素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)
image

【代码】

//堆优化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;
}
posted @ 2024-12-21 13:54  White_ink  阅读(15)  评论(0)    收藏  举报