原题链接:P2502 [HAOI2006]旅行
题意
找到一条$s$到$t$的路径,使得路径上最大边权和最小边权的比值最小。
求这个最小值。
分析
一开始的思路是从大的开始加边,一直加到刚好连通位置,然后dfs求出最小比值。
但是很容易证明算法是错的。
正解是:从大到小钦定这个最大边权,然后往小边权加边,直到刚好连通。
由于加入小的边权之后刚好连通,易证明小边权必定在这条路径上。
但是大的边权不一定在路径上啊?
没关系,我们反正要倒叙枚举,易证如果这个最大边权不在路径上的话,后面肯定能找到一个更小的比值。
这样子,我们只需要每次求出最大边权和最小边权,然后记录比值最小的那一组就可以了。
注意输出格式!
代码
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N=509,M=5009; 4 struct Edge{ 5 int u,v,w; 6 }g[M]; 7 int read(){ 8 char c;int num,f=1; 9 while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0'; 10 while(c=getchar(), isdigit(c))num=num*10+c-'0'; 11 return f*num; 12 } 13 int n,m,s,t,pre[N]; 14 int Maxn=-1,Minn=(1<<31)-1; 15 bool cmp(Edge a,Edge b){return a.w<b.w;} 16 int fid(int x){return (x==pre[x])?x:(pre[x]=fid(pre[x]));} 17 int gcd(int a,int b){return (b==0)?a:gcd(b,a%b);} 18 int main() 19 { 20 n=read();m=read(); 21 for(int i=1;i<=m;i++){ 22 g[i].u=read(); 23 g[i].v=read(); 24 g[i].w=read(); 25 } 26 sort(g+1,g+1+m,cmp); 27 s=read();t=read(); 28 for(int i=m;i>0;i--){ 29 for(int j=1;j<=n;j++)pre[j]=j; 30 int j=i; 31 for(;j>0;j--){ 32 if(fid(g[j].u)!=fid(g[j].v)) 33 pre[fid(g[j].u)]=fid(g[j].v); 34 if(fid(s)==fid(t))break; 35 } 36 if(fid(s)!=fid(t))break; 37 if(Maxn==-1||1.0*g[i].w/g[j].w<1.0*Maxn/Minn){ 38 Maxn=g[i].w; 39 Minn=g[j].w; 40 } 41 } 42 if(Maxn==-1){ 43 printf("IMPOSSIBLE "); 44 return 0; 45 } 46 int d=gcd(Maxn,Minn); 47 Maxn/=d;Minn/=d; 48 printf("%d",Maxn); 49 if(Minn!=1)printf("/%d",Minn); 50 printf(" "); 51 return 0; 52 }