[HAOI2006] 旅行

Posted qjs12

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HAOI2006] 旅行相关的知识,希望对你有一定的参考价值。

  求S-T的路径中最长边与最短边比值最小的路径。

  我不知道怎么做...主要就是这个比值最小,不知道怎么处理,即使看到最小生成树的标签,也没想到咋做......

  解题的思想很简单,只要我们确定了最短边的长度,接下来要做的就是使最长边尽量短。最长边尽量短?很自然地就想到了瓶颈生成树,根据Kruscal算法可以求得一颗瓶颈生成树(同样是一颗最小生成树),因为我们总是先尝试短的边。(一颗瓶颈生成树不一定是最小生成树)对于这道题我们并不需要真的求出生成树,只要跑Kruscal当S,T联通就可以了,于是算法就出来了。

  1:将边排序。

  2:由小到大枚举每条边作为确定的最短边,这条边记为x,因为将边排序了,所以跑Kruscal时可以保证只用比它大的边,当S,T联通时我们停止算法,使S,T联通的这条边记为y。

  3:记录最小的w[y]/w[x],就是答案。

  虽然根据上述算法能够得到正确答案,算法本身也是正确的,但是有一点是要说明的,我们要求的是S-T路径,而x,y是否一定在S-T路径中?

  1:因为y使S,T联通,所以y一定在S-T路径上,上界一定正确。

  2:有的x在S-T路径中而有的并不在。

       假设y在S-T路径中,下界正确,那么上下界都正确,得到的解是合法的。

       假设y不再S-T路径中,又因为y是最短边,所以这个不合法的答案的下界比真实下界小,也就是说在以y作为下界求解的这次过程中,S-T路径上的最短边要比w[y]大,所以这个不合法的解也是不优的。其次,因为不需要这条边依然能使S-T联通,所以我们在之后的枚举过程中,一定能够枚举到真正的下界,使得情况转为1,且得到的解一定比此次的解优。

  综上,我们一定能够得到正解。

 

// q.c

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int M=500+10;
int n,m,s,t,cnt,f[M],ans1=(int)1e9,ans2=1;
struct Edge {
	int u,v,w;
	Edge():u(0),v(0),w(0) {}
	bool operator < (const Edge &A) const {
		return w<A.w;
	}
}ed[M*10];
void add_edge(int a,int b,int c) {
	ed[++cnt].u=a,ed[cnt].v=b,ed[cnt].w=c;
}
void reset() {
	for(int i=1;i<=n;i++) f[i]=i;
}
int found(int x) {
	return f[x]==x?x:f[x]=found(f[x]);
}
int gcd(int a,int b) {
	return !b?a:gcd(b,a%b);
}
int main() {
	freopen("comf.in","r",stdin);
	freopen("comf.out","w",stdout);
	scanf("%d%d",&n,&m);
	reset();
	int a,b,c,fx,fy;
	for(int i=1;i<=m;i++) {
		scanf("%d%d%d",&a,&b,&c);
		add_edge(a,b,c);
		fx=found(a);
		fy=found(b);
		if(fx!=fy) f[fx]=fy;
	}
	scanf("%d%d",&s,&t);
	if(found(s)!=found(t)) {
		printf("IMPOSSIBLE\n");
		return 0;
	}
	sort(ed+1,ed+cnt+1);
	for(int i=1;i<=cnt;i++) {
		reset();
		for(int j=i;j<=cnt;j++) {
			Edge p=ed[j];
			fx=found(p.u);
			fy=found(p.v);
			if(fx!=fy) f[fx]=fy;
			if(found(s)==found(t)) {
				if(ed[j].w*1.0/ed[i].w<ans1*1.0/ans2) {
					ans1=ed[j].w;
					ans2=ed[i].w;
				}
				break;
			}
		}
	}
	int d=gcd(ans1,ans2);
	ans1/=d,ans2/=d;
	if(ans2==1) printf("%d\n",ans1);
	else printf("%d/%d\n",ans1,ans2);
	return 0;
}

 

以上是关于[HAOI2006] 旅行的主要内容,如果未能解决你的问题,请参考以下文章

[HAOI2006]旅行comf

[HAOI2006]旅行(并查集)

[HAOI2006]旅行 题解(kruskal)

BZOJ 1050: [HAOI2006]旅行comf(枚举+并查集)

BZOJ [HAOI2006]旅行comf

[HAOI2006] 旅行