BZOJ1050 [HAOI2006]旅行comf
题目描述:
给你一个无向图,N(N<=500)个顶点, M(M<=5000)条边,每条边有一个权值Vi(Vi<30000)。给你两个顶点S和T,求
一条路径,使得路径上最大边和最小边的比值最小。如果S和T之间没有路径,输出”IMPOSSIBLE”,否则输出这个
比值,如果需要,表示成一个既约分数。 备注: 两个顶点之间可能有多条路径。
题解:
我们想如何贪心。
既然要比值最小,那么我们找到尽量小的最大边和尽量大的最小边就行了。
那怎么找呢?
我们将每个边权排序,每次枚举最小边和最大边,并将其加入并查集,如果将起点和中点连在了同一个联通块中,就是符合答案的一组解了。
附上代码:
#include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; int n,m,s,t,f[5001],flag,idx,cnt1,cnt2; double ans=999999999.0; struct Edge { int l,r,v; }a[5001]; bool cmp(const Edge &x,const Edge &y) { return x.v<y.v; } int find(int p) { if(f[p]!=p) f[p]=find(f[p]); return f[p]; } void merge(int x,int y) { int fx=find(x); int fy=find(y); if(fx!=fy) f[fx]=fy; } int gcd(int x,int y) { if(x%y==0) return y; return gcd(y,x%y); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].v); scanf("%d%d",&s,&t); sort(a+1,a+m+1,cmp); for(int i=1;i<=m;i++) { flag=0; for(int j=1;j<=n;j++) f[j]=j; for(int j=i;j<=m;j++) { merge(a[j].l,a[j].r); if(find(s)==find(t)) { idx=j; flag=1; break; } } if(flag==1) { if(ans>(a[idx].v*1.0)/a[i].v) { ans=(a[idx].v*1.0)/a[i].v; cnt1=a[idx].v; cnt2=a[i].v; } } } if(ans==999999999.0) { printf("IMPOSSIBLE"); return 0; } int k=gcd(cnt1,cnt2); if(cnt2/k==1) printf("%d",cnt1/k); else printf("%d/%d",cnt1/k,cnt2/k); }