[并查集]HAOI2006 旅行

题目描述
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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值