最短路
一.单源最短路
基本算法
(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;
}

浙公网安备 33010602011771号