最短路2 动点spfa
Note:为了避免spfa被卡,我发现动点dij也是行的!
流程
按照一定的顺序逐一将边加入图,用 spfa 维护图的动态 \(dis\)。当然最短路是广义的,可以是瓶颈路等。
具体来说,每次加边就把边的两端加到 queue 中,不清空 \(dis\),跑一遍 spfa。
由于是按照一定顺序加边的,可以钦定当前加入的边是极值,所以该算法常用于解决用两个属性定义的最短路。
例题
【eg1】[NOI2014]魔法森林
按照 \(a\) 从小到大加入边,假定当前加入的边是路径上最大的 \(a\),跑一遍 spfa 求 \(b\) 的瓶颈路(最小的路径上最大值),用 \(maxa+maxb\) 更新答案。
注:虽然现实中路径不一定经过当前边,但是不妨这样假设,因为如果不经过,那么在加入之前的边的时候这个答案已经更新了 ans 了。
再注:唯一可悲的地方在于,spfa 是可以被卡死的,UOJ 上提交你就只有 97.
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=50005,M=1e5+5;
int n,m,dis[N];
bool vis[N];
queue<int>Q;
vector<pair<int,int> >G[N];
struct Edge {int x,y,a,b;}e[M];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)scanf("%d%d%d%d",&e[i].x,&e[i].y,&e[i].a,&e[i].b);
sort(e+1,e+m+1,[](Edge a,Edge b){return a.a<b.a;});
memset(dis,0x3f,sizeof dis);
dis[1]=0;
int ans=0x3f3f3f3f;
for(int i=1;i<=m;i++){
Q.push(e[i].x),Q.push(e[i].y);
G[e[i].x].push_back(make_pair(e[i].y,e[i].b));
G[e[i].y].push_back(make_pair(e[i].x,e[i].b));
while(!Q.empty()){
int x=Q.front();Q.pop();vis[x]=0;
for(pair<int,int>y:G[x]){
if(dis[y.first]>max(dis[x],y.second)){
dis[y.first]=max(dis[x],y.second);
if(!vis[y.first]){
vis[y.first]=1;
Q.push(y.first);
}
}
}
}
ans=min(ans,dis[n]+e[i].a);
}
if(ans==0x3f3f3f3f)puts("-1");
else cout<<ans;
}
【eg2】[HAOI2006]旅行
按照权值从小到大加入边,假定当前加入的边是路径上最大值,跑一遍 spfa 求瓶颈路(最大的路径上最小值),用 \(max\div min\) 更新答案。
当然没有学过动点spfa时更好想的思路显然是跑 kruskal 最小生成树(which is a 瓶颈树),然后固定最小值min找删掉<min的树边后 s~t 的最大值,是 \(O(m^2\alpha)\) 的。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=505,M=5005,INF=0x3f3f3f3f;
int n,m,s,t,dis[N],vis[N],fa[N];
vector<int>vec;
vector<pair<int,int> >G[N];
queue<int>Q;
struct Edge{int x,y,v;}e[M];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void unite(int x,int y){fa[find(y)]=find(x);}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v),vec.push_back(e[i].v);
scanf("%d%d",&s,&t);
sort(vec.begin(),vec.end());
int ana=INF,anb=1;
sort(e+1,e+m+1,[](Edge a,Edge b){return a.v<b.v;});
for(int mn:vec){
for(int i=1;i<=n;i++)G[i].clear(),fa[i]=i;
for(int i=1;i<=m;i++)if(e[i].v>=mn&&find(e[i].x)!=find(e[i].y)){
unite(e[i].x,e[i].y);
G[e[i].x].push_back(make_pair(e[i].y,e[i].v));
G[e[i].y].push_back(make_pair(e[i].x,e[i].v));
}
memset(vis,0,sizeof vis);
vis[s]=1;
Q.push(s);
memset(dis,0x3f,sizeof(dis));
dis[s]=0;
while(!Q.empty()){
int x=Q.front();Q.pop();
for(pair<int,int>y:G[x]){
if(dis[y.first]>max(y.second,dis[x])){
dis[y.first]=max(y.second,dis[x]);
if(vis[y.first])continue;
Q.push(y.first);
vis[y.first]=1;
}
}
}
// for(int i=1;i<=n;i++)printf("%d ",dis[i]); puts("");
if(1ll*dis[t]*anb<1ll*ana*mn)ana=dis[t],anb=mn;
}
if(ana>=1000000ll*anb)puts("IMPOSSIBLE");
else {
int og=__gcd(ana,anb);
ana/=og,anb/=og;
if(anb>1)printf("%d/%d",ana,anb);
else printf("%d",ana);
}
}

浙公网安备 33010602011771号