求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;
}