P2502 [HAOI2006]旅行 - 最小生成树【最小比值生成树(雾】

P2502 [HAOI2006]旅行

Sol:

  • 暴力
    枚举所有从S到T的路径,然后用maxw/minw更新答案。
    时间复杂度:\(O(玄学)\)
  • 正解
    观察到边数\(m\leq5000\)
    考虑由直接求maxw和minw -> 枚举minw求maxw
    由于从S到T的路径上的最大值最小的边一定在最小生成树上(最小生成树的瓶颈路性质),所以我们可以将边从小到大排序,每次枚举边\(e_i\),并将剩下的\(e_i,e_{i+1}\dots e_m\)建最小生成树,当边\(e_k\)使S和T第一次连通时,用\(e_k/e_i\)更新答案。

AC Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int read(){
	int x=0,f=1;char ch=' ';
	while(ch>'9'||ch<'0') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
const int N = 500 + 100,M = 5000 + 100;
int n,m,s,t;
struct node{
	int u,v,w;
	bool operator < (const node& a)const{
		return w<a.w;
	}
}e[M];
int gcd(int a,int b){
	return !b?a:gcd(b,a%b);
}
int fa[N];
int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
double tmpmax;
int ansmax,ansmin;
bool ok;
void kruskal(int l){
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=l;i<=m;i++){
		int u=e[i].u,v=e[i].v,w=e[i].w;
		int p=find(u),q=find(v);
		if(p!=q){
			fa[p]=q;
		}
		if(find(s)==find(t)){
			tmpmax=w;
			ok=0;
			break;
		}
	}
	return ;
}
int main(){
//	freopen("data.in","r",stdin);
//	freopen("sol.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=n;i++) fa[i]=i;
	int tot=0;
	for(int i=1;i<=m;i++){
		int u,v,w;u=read();v=read();w=read();
		e[i]=(node){u,v,w};
		fa[find(u)]=find(v);
	}
	sort(e+1,e+m+1);
	s=read();t=read();
	if(find(s)!=find(t)){
		printf("IMPOSSIBLE");return 0;
	}
	double ans=10000000.0;
	for(int i=1;i<=m;i++){
		ok=1;
		kruskal(i);
		if(ok) continue;//S与T不连通
		double res=tmpmax/e[i].w;
		if(res<ans){
			ansmin=e[i].w;
			ansmax=floor(tmpmax);
			ans=min(ans,res);
		}
	}
	if(ansmax%ansmin==0){
		printf("%d",ansmax/ansmin);
	}
	else{
		int GCD=gcd(ansmin,ansmax);
		ansmin/=GCD,ansmax/=GCD;
		printf("%d/%d",ansmax,ansmin);
	}
	return 0;
}
posted @ 2018-10-08 23:11  dprswdr  阅读(134)  评论(0编辑  收藏  举报