最短路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);
	}
}
posted @ 2022-07-09 20:22  pengyule  阅读(84)  评论(0)    收藏  举报