LOJ题解#136. 最小瓶颈路 DFS+Kruskal

  • 题目链接:

    https://loj.ac/problem/136

  • 思路:

    在我的这篇博客中已经讲到什么是最短瓶颈路,同时给出了一个用Kruskal求最短瓶颈路的一个简洁易懂的方法,然而这道题目可以看作求所有点对的最短瓶颈路,显然那篇博客题解的方法不太管用,于是改进后有了这个算法:

    我们还是先用Kruskal求出最小生成树,同时记录最小生成树中个点与哪些点相连.然后我们把这个最小生成树转化为有根树来进行DFS处理。若正在处理的点编号为\(u\),它的一个后继点(即将DFS)编号\(v\),已经DFS过的一个点编号\(x\),\(ans[i][j]\)表示\(i\)\(j\)的最短瓶颈路的大小。根据定义,显然:

    \(ans[x][v]=ans[v][x]=max(ans[u][v],ans[x][u],w(u,v))\)

    可以看到正在DFS的节点相当于一个转承的作用

    时间复杂度\(O(m\log m+n+k)\)

  • 代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <cmath>
#include <vector>
#include <map>
#include <queue>
#define ll long long 
#define ri register int 
using namespace std;
const int maxn=1005;
const int maxm=100005;
const int inf=0x7fffffff;
template <class T>inline void read(T &x){
	x=0;int ne=0;char c;
	while(!isdigit(c=getchar()))ne=c=='-';
	x=c-48;
	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
	x=ne?-x:x;
	return ;
}
struct Edge{
	int u,v,w;
	Edge(int x,int y,int z){u=x,v=y,w=z;}
	Edge(){u=v=0,w=inf;}
	bool operator <(const Edge &b)const{
		return w<b.w;
	}
}edge[maxm];
int n,m,k;
vector <Edge>g[maxn];
int fa[maxn];
int get(int x){
	if(fa[x]!=x)fa[x]=get(fa[x]);
	return fa[x];
}
void Kruskal(){
	int cnt=0,u,v,w;
	for(ri i=1;i<=n;i++)fa[i]=i;
	for(ri i=1;i<=m;i++){
		u=edge[i].u,v=edge[i].v,w=edge[i].w;
		u=get(u),v=get(v);
		if(u!=v){
			fa[u]=v;
			g[u].push_back(Edge(u,v,w));
			g[v].push_back(Edge(v,u,w));
			cnt++;
			if(cnt==n-1)break;
		}
	}
	return ;
}
bool vis[maxn];
int ans[maxn][maxn];
void dfs(int now){
	int u,v,w;
	vis[now]=1;
	for(ri i=0;i<g[now].size();i++){
		v=g[now][i].v,w=g[now][i].w;
		if(!vis[v]){
			for(ri j=1;j<=n;j++){
				if(vis[j])//如果它在有根树中已被访问过 
				{
					ans[j][v]=ans[v][j]=max(ans[now][v],max(ans[now][j],w));
				}
			}
			dfs(v);
		}	
	}
	return ;
}
int main(){
	int u,v,w;
	read(n),read(m),read(k);
	for(ri i=1;i<=m;i++){
		read(u),read(v),read(w);
        edge[i].u=u,edge[i].v=v,edge[i].w=w;
	}
	sort(edge+1,edge+1+m);
	Kruskal();
	memset(ans,0,sizeof(ans));
	memset(vis,0,sizeof(vis));
	dfs(1);
	while(k--){
		read(u),read(v);
		if(!ans[u][v])puts("-1");
		else printf("%d\n",ans[u][v]);
	}
	return 0;
}
  • 注:

    1. 这个算法来自这里,但是他的代码中有一个错误就是上文提到\(ans[i][j]\)的转移

    2. 有兴趣的可以看一下增强版:https://loj.ac/problem/137

posted @ 2018-06-18 13:20  Rye_Catcher  阅读(352)  评论(0编辑  收藏  举报