[HAOI2006]旅行
题目: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\n",mx/mn);else{
int l=gcd(mx,mn);
printf("%d/%d\n",mx/l,mn/l);
}
return 0;
}
注意代码第23行的优化。为什么可以这么优化呢?因为这条边与上一条边权值相同,在上一条边作最小边时,这条边已经被加进去过了,所以得到的答案不可能更优,或者是直接连通,比值为1,也不可能更优。所以可以这么优化。
这是在BZOJ中优化前和优化后的对比(上为优化后):

可以看到效果挺好的。

浙公网安备 33010602011771号