最短路

一.单源最短路

基本算法

(1) SPFA

代码示例:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,s;
vector<pair<int ,int > > gra[10100];
queue<pair<int,int> > q;
int dis[10100];
int inf=1e10;
int vis[10100];
signed main()
{
	cin >> n >> m >> s;
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin >> u >> v >> w;
		gra[u].push_back({v,w});
	}
	for(int i=1;i<=n;i++) dis[i] = inf;
	//dis[s] = 0;
	q.push({s,0});
	while(!q.empty()){
		pair<int,int> head = q.front();
		q.pop();
		if(head.second < dis[head.first]){
			vis[head.first]++;
			dis[head.first]=head.second;
			for(pair<int,int> x:gra[head.first]){
				q.push({x.first,x.second+head.second});
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(dis[i]!=inf)	cout << dis[i] <<' ';
		else cout << (1ll<<31)-1 << ' ';
	}
	return 0;
 } 

在随机状态下 该程序的复杂度为O(km)

k近似为点的平均进队次数

通过以下代码探究随机状态下k的估算:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,s;
vector<pair<int ,int > > gra[10100];
queue<pair<int,int> > q;
int dis[10100];
int inf=1e10;
int vis[10100];
int RAND(){
	return (rand()%n)+1;
}
signed main()
{
	srand((unsigned)time(NULL));
	n = 1000;
	m = 10000;
	s=1;
	//cin >> n >> m >> s;
	for(int i=1;i<=m;i++){
		int u,v,w;
		u = RAND();
		v = RAND();
		while(u==v) v = RAND();
		w = (rand()%1000)+1;
		gra[u].push_back({v,w});
	}
	for(int i=1;i<=n;i++) dis[i] = inf;
	//dis[s] = 0;
	q.push({s,0});
	while(!q.empty()){
		pair<int,int> head = q.front();
		q.pop();
		if(head.second < dis[head.first]){
			vis[head.first]++;
			dis[head.first]=head.second;
			for(pair<int,int> x:gra[head.first]){
				q.push({x.first,x.second+head.second});
			}
		}
	}
	int summ=0;
	for(int i=1;i<=n;i++){
		summ+=vis[i];
	}
	cout << (double)summ/n << endl;
	return 0;
 } 

数据:

n = 1000, m=100000,k平均为9.08

n = 1000,m=10000 ,k平均为4.79

n = 1000, m=1000, k平均为0.02

n = 1000, m=100,k平均为0.01

n = 100, m=10000,k平均为7.21

n = 100,m=1000 ,k平均为3.77

n = 100, m=100, k平均为0.62

n = 100, m=10,k平均为0.01

n=1000时 k-m/n图

n=100时

分析可得出
\(m = 0.1n\)\(k ≈ 10^-\) $ ^3$

\(m = n\) 时 $k ≈ 1 $

\(m = 10n\) 时 $k ≈ 5 $

\(m = 100n\) 时 $k ≈ 10 $

(2)Dijkstra

堆优化复杂度O(m log n)

一些灵活用法:

(1)分层图最短路

例如:P1948 [USACO08JAN] Telephone Lines S

方法:在dp数组上增加一维,并使用spfa更新

(2)反图

需要计算所有点到某个点的最短路时

可以将边反过来,转化为定点到所有点的最短路

例如:P1073 [NOIP2009 提高组] 最优贸易

(3)当所有负边都具有某些性质

可以正边(非性质边)构成的强连通分量内使用Dj

每个分量间用性质相关算法

一.全源最短路

Floyed算法

\(O(n^3)\)

for(int k=1;k<=n;k++)
{
    for(int i=1;i<=n;i++)
    {
         for(int j=1;j<=n;j++)
         {
           f[i][j] = min(f[i][j],f[i][k]+f[k][j]);
         }
    }
}

当k枚举完到某个值k'时

f中存储的最短路路径中的点编号不超过k

(1) floyed算法求解无向图的最小环问题

在floyed更新路径k前,枚举有关k的环

#include<bits/stdc++.h>
using namespace std;
int n,m;
int f[110][110];
int a[110][110];
int inf = 100*100000 + 10;
int ans;
int main()
{
	cin >> n >> m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i!=j) a[i][j] = inf;
		}
	}
	for(int i=1;i<=m;i++){
		int u,v,d;
		cin >> u >> v >> d;
		a[u][v] = min(a[u][v],d);
		a[v][u] = min(a[v][u],d);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			f[i][j] = a[i][j];
		}
	}
	ans=inf;
	for(int k=1;k<=n;k++){
		for(int i=1;i<k;i++){
			for(int j=i+1;j<k;j++){
				ans = min(ans,f[i][j]+a[i][k]+a[k][j]);
			} 
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				f[i][j] = min(f[i][j],f[i][k]+f[k][j]);
			}
		}
	}
	if(ans!=inf) cout << ans << endl;
	else cout << "No solution." << endl;
	return 0;
}

注意:根据对称性,枚举环的时候i,j都小于k并不会影响结果

(2)矩阵乘法加速floyed

每个内循环floyed,就会多经过一条边

当要计算经过恰好n条边的最短路时,要做n次floyed

由于n次内层foled等效于两个(n-1)次floyed数组合并

我们可以利用二进制分解,将n次转化为log n次

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,t,s,e;
int w[110],u[110],v[110];
int lsh[210],top=0;
int f[210][210];
int ret[210][210];
int jc[210][210];
bool flag=0;
const int inf = 1e18;
void ftf(){
	for(int i=1;i<=top;i++){
		for(int j=1;j<=top;j++){
			jc[i][j] = inf;
		}
	}
	for(int k=1;k<=top;k++){
		for(int i=1;i<=top;i++){
			for(int j=1;j<=top;j++){
				jc[i][j] = min(jc[i][j],f[i][k]+f[k][j]);
			}
		}
	}
	for(int i=1;i<=top;i++){
		for(int j=1;j<=top;j++){
			f[i][j] = jc[i][j];
		}
	}
	return;
}
void rtf(){
	if(flag==0){
		for(int i=1;i<=top;i++){
			for(int j=1;j<=top;j++){
				ret[i][j] = f[i][j];
			}
		}
		flag=1;
		return;
	
		
	}
	else{
		for(int i=1;i<=top;i++){
			for(int j=1;j<=top;j++){
				jc[i][j] = inf;
			}
		}
		for(int k=1;k<=top;k++){
			for(int i=1;i<=top;i++){
				for(int j=1;j<=top;j++){
					jc[i][j] = min(jc[i][j],ret[i][k]+f[k][j]);
				}
			}
		}
		for(int i=1;i<=top;i++){
			for(int j=1;j<=top;j++){
			ret[i][j] = jc[i][j];
		}
	}
	}
}
void fast_pow(int b){
	while(b){
		if(b&1) rtf();
		ftf();//f = f*f;
		b>>=1;
	}
	return;
}
signed main()
{
	cin >> n >> t >> s >> e;
	for(int i=1;i<=t;i++){
		cin >> w[i] >> u[i] >> v[i];
		lsh[++top] = u[i];
		lsh[++top] = v[i];
	}
	sort(lsh+1,lsh+1+t+t);	
	top = unique(lsh+1,lsh+1+t+t)-lsh-1;
	for(int i=1;i<=t;i++){
		u[i] = lower_bound(lsh+1,lsh+1+top,u[i])-lsh;
		v[i] = lower_bound(lsh+1,lsh+1+top,v[i])-lsh;
	}
	for(int i=1;i<=top;i++){
		for(int j=1;j<=top;j++){
		 f[i][j] = inf;
		}
	}
	for(int i=1;i<=t;i++){
		f[u[i]][v[i]]=w[i];
		f[v[i]][u[i]]=w[i];
	}
	fast_pow(n);
	s = lower_bound(lsh+1,lsh+1+top,s)-lsh;
	e = lower_bound(lsh+1,lsh+1+top,e)-lsh;
	cout << ret[s][e] << endl;
	return 0;
}
posted @ 2025-08-17 20:38  S/P/A/  阅读(10)  评论(1)    收藏  举报