BZOJ 1050 旅行comf
题目如下:
题目描述
给你一个无向图,N(N<=500)个顶点, M(M<=5000)条边,每条边有一个权值Vi(Vi<30000)。给你两个顶点S和T,求一条路径,使得路径上最大边和最小边的比值最小。如果S和T之间没有路径,输出”IMPOSSIBLE”,否则输出这个比值,如果需要,表示成一个既约分数。 备注: 两个顶点之间可能有多条路径。
输入
第一行包含两个正整数,N和M。 下来的M行每行包含三个正整数:x,y和v。表示景点x到景点y之间有一条双向公路,车辆必须以速度v在该公路上行驶。 最后一行包含两个正整数s,t,表示想知道从景点s到景点t最大最小速度比最小的路径。s和t不可能相同。
输出
如果景点s到景点t没有路径,输出“IMPOSSIBLE”。否则输出一个数,表示最小的速度比。如果需要,输出一个既约分数。
样例输入
【样例输入1】
4 2
1 2 1
3 4 2
1 4
【样例输入2】
3 3
1 2 10
1 2 5
2 3 8
1 3
【样例输入3】
3 2
1 2 2
2 3 4
1 3
样例输出
【样例输出1】
IMPOSSIBLE
【样例输出2】
5/4
【样例输出3】
2
用结构体存边:E[ ].fr(起始点) E[ ].to(指向点) E[ ].cost(权值)
此题用的是并查集来做的。首先对所有的边进行从小到大排序,定义ans1=inf为当前寻找到的最大边的最小值,ans2=1位当前的最小边。然后从小到大进行枚举(循环一:for(i=1;i<=m;i++))在循环一中建立以点为关键字的并查集数组。再进行第二重循环(for(int j=i;j<=n;j++)),该循环是从循环一进行到的边的编号开始,到最大的边,每添加一条边,将其开头和结尾添加在并查集中,再检测S和T的连通性即(if(find(S)==find(T)){ }),若联通则比较ans1/ans2与E[ j ].cost/E[ i ].cost的大小比较过程中可以交叉相乘,这样可以避免整除的问题。若新的比值更小则更新ans1和ans2的值并跳出循环二。若不连通依然比较ans1/ans2与E[ j ].cost/E[ i ].cost的大小,若新的比值小于原存的比值,则跳出循环二,因为循环二中E[j].cost是不断增大的。循环一结束后,主体就结束了,最后的输出一看就懂。
#include
<cstdio>
#include
<algorithm>#define inf 10000000
using namespace std; struct edge{ int fr,to,cost; }; edge E[5001]; inline int comp(edge a,edge b) { return a.cost<b.cost; } int m,n,s,t; int fa[501]; inline int find(int x) { if(fa[x]!=x) fa[x]=find(fa[x]); return fa[x]; } inline int gcd(int a,int b)//a>b { if(b==0) return a; return gcd(b,a%b); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&E[i].fr,&E[i].to,&E[i].cost); scanf("%d%d",&s,&t); sort(E+1,E+m+1,comp); int ans1=inf,ans2=1; for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++) fa[j]=j; for(int j=i;j<=m;j++){ int f1=find(E[j].fr),f2=find(E[j].to); fa[f1]=f2; if(find(s)==find(t)){ if(E[i].cost*ans1>E[j].cost*ans2) ans1=E[j].cost,ans2=E[i].cost; break; } if(E[i].cost*ans1<E[j].cost*ans2) break; } } int zdg=gcd(ans1,ans2); if(ans1==inf) printf("IMPOSSIBLE"); else if(ans2!=zdg) printf("%d/%d",ans1/zdg,ans2/zdg); else printf("%d",ans1/ans2); }