【总结】最短路模板+最小路径生成树

Dijkstra

O ( ( n + m ) l o g n ) O((n+m)logn) O((n+m)logn)
只能用于正权图

#include<bits/stdc++.h>
using namespace std;
int n,m,st,ed,dis[2505];
bool vis[2505];
struct edge{
    
    
	int v,w;
	edge(){
    
    }
	edge(int V,int W) {
    
    v=V,w=W;}
};

void read(int &x) {
    
    
	int f=1;x=0;char c=getchar();
	while(c<'0'||c>'9') {
    
    if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {
    
    x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	x*=f;
}

priority_queue<pair<int,int> > q;
vector<edge> v[2505];

void dijkstra() {
    
    
	q.push(make_pair(0,st));
	dis[st]=0;
	while(q.size()) {
    
    
		int x=q.top().second;q.pop();
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=0;i<v[x].size();i++) {
    
    
			int vi=v[x][i].v,wi=v[x][i].w;
			if(dis[x]+wi<dis[vi]) {
    
    //松弛操作 
				dis[vi]=dis[x]+wi;
				q.push(make_pair(-dis[vi],vi));
			}
		}
	}
}
int main() {
    
    
	memset(dis,0x3f,sizeof(dis));
	cin>>n>>m>>st>>ed;
	for(int i=1;i<=m;i++) {
    
    
		int x,y,z;
		cin>>x>>y>>z;
		v[x].push_back(edge(y,z));
		v[y].push_back(edge(x,z));
	}
	dijkstra();
	printf("%d",dis[ed]);
}

Bellman-Ford

O ( n m ) O(nm) O(nm)

如果n-1次松弛后仍然不满足三角形不等式,则说明有负环(这个环内每次都可以更新,因为一次比一次小),否则就没有负环(可以收敛)。

注意:有负环时bellman算法已经崩溃了,不能作为任何答案,所以虽然在负权下可以工作,但容易RE

判环+输出路径

#include<bits/stdc++.h>
using namespace std;
int n,m,st,ed,dis[505],num[505],pre[505];
struct Edge{
    
    
	int u,v,w;
}e[20005];

void print(int x) {
    
    
	if(!pre[x]) {
    
    
		printf("%d ",x);
		return;
	}
	print(pre[x]);
	printf("%d ",x);
}
bool bellman_ford(int S) {
    
    
	dis[S]=0;
	num[S]=0;
	pre[S]=0;
	for(int i=1;i<n;i++) {
    
    
		for(int j=1;j<=m;j++) {
    
    
			int u=e[j].u,v=e[j].v,w=e[j].w;
			if(dis[v]>dis[u]+w||(dis[v]==dis[u]+w&&num[v]>num[u]+1)||(dis[v]==dis[u]+w&&num[v]==num[u]+1&&u<pre[v])) {
    
    
				dis[v]=dis[u]+w;
				num[v]=num[u]+1;
				pre[v]=u;
			}
		}
	}
	for(int j=1;j<=m;j++) {
    
    
		int u=e[j].u,v=e[j].v,w=e[j].w;
		if(dis[v]>dis[u]+w) {
    
    
			return 0;
		}
	}
	return 1;
}
int main() {
    
    
	memset(dis,0x3f,sizeof(dis));
	memset(num,0x3f,sizeof(num));
	memset(pre,0x3f,sizeof(pre));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) {
    
    
		scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
	}
	scanf("%d%d",&st,&ed);
	if(bellman_ford(st)) {
    
    
		printf("%d\n",dis[ed]);
		print(ed);
	}
	else printf("No Solution");
}

Spfa

O ( k n ) O(kn) O(kn),但容易被卡(尤其是稠密图)

基于队列优化的bellman算法,负权图同样可以工作

可以用 n u m [ ] num[] num[]数组记录路径长度,只要有松弛操作就令 n u m [ v ] = n u m [ u ] + 1 num[v]=num[u]+1 num[v]=num[u]+1,若 n u m [ v ] > n num[v]>n num[v]>n则说明有负环

#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
int n,m,dis[maxn];
bool vis[maxn];

void read(int &x) {
    
    
	int f=1;x=0;char c=getchar();
	while(c<'0'||c>'9') {
    
    if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {
    
    x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	x*=f;
}

struct edge{
    
    
	int v,w;
	edge(){
    
    }
	edge(int V,int W) {
    
    
		v=V,w=W;
	}
};

queue<int> q;
vector<edge> v[maxn];

void spfa() {
    
    
	q.push(1);
	dis[1]=0;
	vis[1]=1;
	while(q.size()) {
    
    
		int x=q.front();q.pop();
		vis[x]=0;
		for(int i=0;i<v[x].size();i++) {
    
    
			int vi=v[x][i].v,wi=v[x][i].w;
			if(dis[x]+wi<dis[vi]) {
    
    
				dis[vi]=dis[x]+wi;
				if(!vis[vi]) {
    
    
					q.push(vi);
					vis[vi]=1;
				}
			}
		}
	}
}
int main() {
    
    
	memset(dis,0x3f,sizeof(dis));
	read(n),read(m);
	for(int i=1;i<=m;i++) {
    
    
		int x,y,z;
		read(x),read(y),read(z);
		v[x].push_back(edge(y,z));
		v[y].push_back(edge(x,z));
	}
	spfa();
	printf("%d",dis[n]);
}

最小路径生成树

eg.黑暗城堡

解析:对于1到 i i i的最短路,事实上存在 j j j使 d i s [ i ] = d i s [ j ] + G ( j , i ) dis[i]=dis[j]+G(j,i) dis[i]=dis[j]+G(j,i)

这给我们启发: j j j的最短路与 i i i的最短路存在联系

如果把每一个 i i i与前继 j j j连起来,可以发现一定找得到这个 j j j,那么 i i i的最短路一定由 j j j贡献,就只需要考虑 j j j的最短路了。

题目又告诉我们这是一个树形结构,且没有负边,边是n-1,刚好n个点,所以每个点 i i i只能找一个前继点 j j j,我们只需统计num[i]表示满足条件的j的个数,然后把num[1~n]相乘即可。

至于求dis[i],可以用dijkstra预处理完成。

猜你喜欢

转载自blog.csdn.net/cqbzlydd/article/details/107673753