Living-Dream 系列笔记 第41期

Posted on 2024-03-02 16:48  _XOFqwq  阅读(3)  评论(0编辑  收藏  举报

稍微讲一下 dijkstra qwq。

dijkstra、bellman-ford(or spfa)、bfs 的区别:

  • dijkstra 以为研究对象;

  • bellman-ford / spfa 则以为研究对象;

  • 而 bfs 可以看作边权为 \(1\)(or 全都相同)的 dijkstra,因此它可以用队列实现。

dijkstra 的具体实现:

  • 将所有点分为两类(黑点 / 白点),分别代表已确定 / 未确定最短路的点。

  • 每次在白点中选取与起点距离最近的点 \(u\) 染黑,并对所有 \(u \to v\) 进行松弛操作。

其中,距离取 \(\min\) 的操作若直接找是 \(O(n^2)\) 的,套个堆就能做到 \(O(n \log n)\)

dijkstra 的理论依据(以下简称 引理):

  • 在一张仅有正边权的图中,对于一白点 \(u\),若它在 dijkstra 中被选中了,则一定不存在另一白点 \(v\),使得 \(dis_{s,v}+dis_{v,u}<dis_{s,u}\)\(s\) 为起点,\(dis_{u,v}\) 表示 \(u,v\) 之间的最短路)。

  • 证明:

    采用反证法。

    假设存在不同于 \(u\) 的一白点 \(v\),使得 \(dis_{s,v}+dis_{v,u}\)\(dis_{s,u}\) 更短,

    因为 \(u\) 在 dijkstra 中被选上了,根据 dijkstra 的实现过程,可得 \(dis_{s,u}<dis_{s,v}\)

    又因图中仅有正边权,所以有 \(dis_{s,u}<dis_{s,v}+dis_{v,u}\),这与假设不符,命题得证。

    证毕

dijkstra 的注意事项:

  • dijkstra 无法用于正负交替边权(全负边权可以转正数后跑)。

  • dijkstra 无法跑最长路。

  • 以上均可用 引理 来解释,在此不再赘述。

T1

brute-force dijkstra 板子。

#include<bits/stdc++.h>
using namespace std;

const int N=1e4+5,M=5e5+5;
int n,m,s;
int dis[N];
bool vis[N];
struct node{ int v,w; };
vector<node> G[M];

void dijkstra(){
	memset(dis,0x3f,sizeof(dis));
	dis[s]=0;
	for(int i=1;i<=n;i++){
		int mini=1e9,id=0;
		for(int j=1;j<=n;j++)
			if(mini>dis[j]&&!vis[j]) mini=dis[j],id=j;
		vis[id]=1;
		for(node j:G[id])
			if(dis[j.v]>dis[id]+j.w)
				dis[j.v]=dis[id]+j.w;
	}
}

int main(){
	cin>>n>>m>>s;
	for(int i=1,u,v,w;i<=m;i++){
		cin>>u>>v>>w;
		G[u].push_back({v,w});
	}
	dijkstra();
	for(int i=1;i<=n;i++) cout<<(dis[i]==0x3f3f3f3f?2147483647:dis[i])<<' ';
	return 0;
} 

T2

堆优化 dijkstra 板子。

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e5+5,M=2e5+5;
int n,m,s;
int dis[N];
bool vis[N];
struct Edge{ int v,w; };
struct Node{
	int u,w;
	bool operator < (const Node &b) const{
		return w>b.w;
	}
};
vector<Edge> G[M];

inline int read() {
	int x = 0, f = 1; char ch = getchar();
    while (ch > '9' || ch < '0') { if (ch == '-')f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
inline void write(int x) {
    if (x < 0) { putchar('-'); x = -x; }
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
}

void dijkstra(){
	memset(dis,0x3f,sizeof(dis));
	dis[s]=0;
	priority_queue<Node> pq;
	pq.push({s,0});
	while(!pq.empty()){
		Node cur=pq.top(); pq.pop();
		if(vis[cur.u]) continue; vis[cur.u]=1;
		for(Edge j:G[cur.u])
			if(dis[j.v]>dis[cur.u]+j.w)
				dis[j.v]=dis[cur.u]+j.w,
				pq.push({j.v,dis[j.v]});
	}
}

signed main(){
	n=read(),m=read(),s=read();
	for(int i=1,u,v,w;i<=m;i++){
		u=read(),v=read(),w=read();
		G[u].push_back({v,w});
	}
	dijkstra();
	for(int i=1;i<=n;i++) write(dis[i]),putchar(' ');
	return 0;
} 

T3

无向图上的堆优化 dijkstra 板子。

#include<bits/stdc++.h>
using namespace std;

int n,m,s,t;
int dis[3031];
bool vis[3031];
struct Edge{ int v,w; };
struct Node{ 
	int u,w;
	bool operator < (const Node &b) const{
		return w>b.w;
	} 
};
vector<Edge> G[7031];

void dijkstra(){
	memset(dis,0x3f,sizeof(dis)),dis[s]=0;
	priority_queue<Node> pq; pq.push({s,0});
	while(!pq.empty()){
		Node now=pq.top(); pq.pop();
		if(vis[now.u]) continue; vis[now.u]=1;
		for(auto i:G[now.u]){
			if(dis[i.v]>dis[now.u]+i.w&&i.v!=now.u)
				dis[i.v]=dis[now.u]+i.w,
				pq.push({i.v,dis[i.v]});
		}
	}
}

int main(){
	cin>>n>>m>>s>>t;
	for(int i=1,u,v,w;i<=m;i++)
		cin>>u>>v>>w,
		G[u].push_back({v,w}),
		G[v].push_back({u,w});
	dijkstra();
	cout<<dis[t];
	return 0;
}

T4

\(2 \times n\) 遍 dijkstra 即可。

似乎有更优做法,但是我先咕着。

#include<bits/stdc++.h>
using namespace std;

int n,m,ans;
int dis[3031];
bool vis[3031];
struct Edge{ int v,w; };
struct Node{ 
	int u,w;
	bool operator < (const Node &b) const{
		return w>b.w;
	} 
};
vector<Edge> G[7031];

void dijkstra(int s){
	memset(vis,0,sizeof(vis));
	memset(dis,0x3f,sizeof(dis)),dis[s]=0;
	priority_queue<Node> pq; pq.push({s,0});
	while(!pq.empty()){
		Node now=pq.top(); pq.pop();
		if(vis[now.u]) continue; vis[now.u]=1;
		for(auto i:G[now.u]){
			if(dis[i.v]>dis[now.u]+i.w&&i.v!=now.u)
				dis[i.v]=dis[now.u]+i.w,
				pq.push({i.v,dis[i.v]});
		}
	}
}

int main(){
	cin>>n>>m;
	for(int i=1,u,v,w;i<=m;i++) 
		cin>>u>>v>>w,G[u].push_back({v,w});
	for(int i=2;i<=n;i++)
		dijkstra(1),ans+=dis[i],
		dijkstra(i),ans+=dis[1];
	cout<<ans;
	return 0;
}

T5

容易发现开关初始状态边权为 \(0\),其余边权均为 \(1\)

于是如此建图跑 dijkstra 即可。

#include<bits/stdc++.h>
using namespace std;

int n,a,b,ans;
int dis[131];
bool vis[131];
struct Edge{ int v,w; };
struct Node{ 
	int u,w;
	bool operator < (const Node &b) const{
		return w>b.w;
	} 
};
vector<Edge> G[10031];

void dijkstra(int s){
	memset(vis,0,sizeof(vis));
	memset(dis,0x3f,sizeof(dis)),dis[s]=0;
	priority_queue<Node> pq; pq.push({s,0});
	while(!pq.empty()){
		Node now=pq.top(); pq.pop();
		if(vis[now.u]) continue; vis[now.u]=1;
		for(auto i:G[now.u]){
			if(dis[i.v]>dis[now.u]+i.w&&i.v!=now.u)
				dis[i.v]=dis[now.u]+i.w,
				pq.push({i.v,dis[i.v]});
		}
	}
}

int main(){
	cin>>n>>a>>b;
	for(int i=1,k;i<=n;i++){
		cin>>k;
		for(int j=1,v;j<=k;j++)
			cin>>v,G[i].push_back({v,(j==1?0:1)});
	}
	dijkstra(a),cout<<(dis[b]==0x3f3f3f3f?-1:dis[b]);
	return 0;
}

T6

边权设为 \(1-w \times 0.01\),倒序从 \(B\) 推算出 \(A\) 即可。

#include<bits/stdc++.h>
using namespace std;

int n,m,a,b,ans;
double dis[2031];
bool vis[2031];
struct Edge{ int v; double w; };
struct Node{ 
	int u; double w;
	bool operator < (const Node &b) const{
		return w>b.w;
	} 
};
vector<Edge> G[200031];

void dijkstra(int s){
	memset(vis,0,sizeof(vis));
	for(int i=0;i<=n+1;i++) dis[i]=2147483647.0;
	dis[s]=100.0;
	priority_queue<Node> pq; pq.push({s,100.0});
	while(!pq.empty()){
		Node now=pq.top(); pq.pop();
		if(vis[now.u]) continue; vis[now.u]=1;
		for(auto i:G[now.u]){
			if(dis[i.v]>dis[now.u]/i.w)
				dis[i.v]=dis[now.u]/i.w,pq.push({i.v,dis[i.v]});
		}
	}
}

int main(){
	//freopen("P1576_1.in","r",stdin);
	cin>>n>>m;
	for(int i=1,u,v,w;i<=m;i++)
		cin>>u>>v>>w,G[u].push_back({v,1.0-w*0.01}),G[v].push_back({u,1.0-w*0.01});	
	cin>>a>>b;
	dijkstra(b),cout<<setprecision(8)<<fixed<<dis[a];
	return 0;
}