您的位置:首页 > 其它

[并查集]HAOI2006 旅行

2017-12-15 20:55 337 查看
题目描述

Z小镇是一个景色宜人的地方,吸引来自各地的观光客来此旅游观光。Z小镇附近共有N个景点(编号为1,2,3,…,N),这些景点被M条道路连接着,所有道路都是双向的,两个景点之间可能有多条道路。也许是为了保护该地的旅游资源,Z小镇有个奇怪的规定,就是对于一条给定的公路Ri,任何在该公路上行驶的车辆速度必须为Vi。速度变化太快使得游客们很不舒服,因此从一个景点前往另一个景点的时候,大家都希望选择行使过程中最大速度和最小速度的比尽可能小的路线,也就是所谓最舒适的路线。

Input

第一行包含两个正整数,N和M。

接下来的M行每行包含三个正整数:x,y和v。表示景点x到景点y之间有一条双向公路,车辆必须以速度v在该公路上行驶。

最后一行包含两个正整数s,t,表示想知道从景点s到景点t最大最小速度比最小的路径。s和t不可能相同。

Output

如果景点s到景点t没有路径,输出“IMPOSSIBLE”。否则输出一个数,表示最小的速度比。如果需要,输出一个既约分数。

1<N<=500

1<=x,y<=N,0<v<30000,x≠y

0<M<=5000

分析

这题看了一下,数据不大,可以m^2枚举。

但是如何判断是最大最小比呢?我想到了排序

排序以后,就很容易了,可是还有一个问题,如果要查找是否合并的话,要一个m^2的邻接矩阵,肯定MLE的

并查集可解此问题。

1、排序(由小到大)

2、每次枚举一个景点,当并查集发现起点与终点已经连接以后,退出第二层枚举(或者所有点都枚举一遍仍无法连接可以退出)

3、把最大值和最小值的比值与原最大值和最小值的比值对比,更新

4、欧几里德约分即可

(温馨提示,比值等式列为a:b=c:d,为求精确度我转化为乘法ad=bc)

#include <iostream>
#include <cstdio>
using namespace std;
int n,m,a[5001][3],i,j,f[501],mx,mn,s,t;
void qs(int l,int h)
{
int i=l,j=h,mid=a[(l+h)/2][2],t;
if (l>=h) return;
do
{
while (a[i][2]<mid) i++;
while (a[j][2]>mid) j--;
if (i<=j)
{
t=a[i][0];a[i][0]=a[j][0];a[j][0]=t;
t=a[i][1];a[i][1]=a[j][1];a[j][1]=t;
t=a[i][2];a[i][2]=a[j][2];a[j][2]=t;
i++;j--;
}
}
while (i<=j);
qs(i,h);
qs(l,j);
}
int fin(int x)
{
int i=x,root;
while (f[i]!=i)
i=f[i];
root=i;
i=x;
while (f[i]!=i)
{
f[i]=root;
i=f[i];
}
return root;
}
void uni(int x,int y)
{
int i=fin(x),j=fin(y);
f[i]=f[j];
}
int gcd(int a,int b)
{
if (b==0) return a;
return gcd(b,a%b);
}
int main()
{
scanf("%d%d",&n,&m);
for (i=1;i<=m;i++)
scanf("%d%d%d",&a[i][0],&a[i][1],&a[i][2]);
scanf("%d%d",&s,&t);
qs(1,m);
mx=5000;mn=1;
for (i=1;i<=m;i++)
{
for (j=1;j<=n;j++) f[j]=j;
j=i-1;
while (j<=m&&fin(s)!=fin(t))
{
j++;
uni(a[j][0],a[j][1]);
}
if (fin(s)!=fin(t))
break;
if (a[j][2]*mn<a[i][2]*mx)
{
mx=a[j][2];
mn=a[i][2];
}
}
if (mx==5000)
{
printf("IMPOSSIBLE");
return 0;
}
int q=gcd(mx,mn);
mx/=q;mn/=q;
if (mx%mn==0)
printf("%d",mx/mn);
else
printf("%d/%d",mx,mn);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: