题目:BZOJ1050、codevs1001、洛谷P2502。
题目大意:见BZOJ的题目描述。
解题思路:首先对所有的边按权值排序,然后枚举最小边i,对i~m的所有边,一条一条加进去,当s和t连通时(并查集维护)结束加边并判断最大边和最小边i的比值是否比当前答案较小,如果是则更新。最后得出的即为答案。如果s和t无法连通输出“IMPOSSIBLE”即可。时间复杂度$O(m^2)$。
C++ Code:
#include<algorithm> #include<cstdio> using namespace std; int n,m,s,t; struct Edges{ int from,to,dis; bool operator <(const Edges&rhs)const{return dis<rhs.dis;} }e[5050]; int fa[505]; int dad(int x){return (x==fa[x])?(x):(fa[x]=dad(fa[x]));} 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",&e[i].from,&e[i].to,&e[i].dis); scanf("%d%d",&s,&t); sort(e+1,e+1+m); int mx=0,mn=2000000000; for(int i=1,j;i<=m;++i){ if(e[i].dis==e[i-1].dis)continue; for(j=1;j<=n;++j)fa[j]=j; bool flag=false; for(j=i;j<=m;++j){ int a=dad(e[j].from),b=dad(e[j].to); if(a!=b)fa[b]=a; if(flag=(dad(s)==dad(t)))break; } if(flag&&(mn>mx||(double)mx/(double)mn>(double)e[j].dis/(double)e[i].dis))mx=e[j].dis,mn=e[i].dis; } if(mn>mx)puts("IMPOSSIBLE");else if(!(mx%mn))printf("%d ",mx/mn);else{ int l=gcd(mx,mn); printf("%d/%d ",mx/l,mn/l); } return 0; }
注意代码第23行的优化。为什么可以这么优化呢?因为这条边与上一条边权值相同,在上一条边作最小边时,这条边已经被加进去过了,所以得到的答案不可能更优,或者是直接连通,比值为1,也不可能更优。所以可以这么优化。
这是在BZOJ中优化前和优化后的对比(上为优化后):
可以看到效果挺好的。