走亲访友(并查集的应用)

又到了过节的时候了,少不了的就是拜访亲友,小丁也是如此。

小丁这次要去姥姥家,因为没有直达的路,所以小丁要通过坐车在一些站点进行中转。为了使旅程不那么无聊,小丁想要这一路上坐车最长的时间和坐车最短的时间的比值尽可能的小。

输入格式:

第一行输入两个正整数N,M,分别代表站点的个数以及所有站点之间路的条数。

接下来的 M行每行包括三个正整数u,v,w,表示从站点u坐车到站点v需要w单位的时间。当然,从站点v坐车到站点u也需要w单位的时间。

最后一行输入两个正整数分别代表小丁家所在的站点和小丁姥姥家所在的站点。

数据保证有 1<N≤500,1≤u,v≤N,1≤w≤30000,1≤M≤5000。

输出格式:

若小丁不能到达姥姥家,输出 IMPOSSIBLE,否则输出一个数代表最小的时间比。

如果需要,输出一个最简分数。

输入样例1:

4 2
1 2 1
3 4 2
1 4

输出样例1:

IMPOSSIBLE

输入样例2:

3 3
1 2 10
1 2 5
2 3 8
1 3

输出样例2:

5/4

输入样例3:

3 2
1 2 2
2 3 4
1 3

输出样例3:

2

 题意:第一行给出n和m代表站点数和站点间的通路数,接下来m行,每行给出u,v,w分别表示一条公交的两个站点(起点和终点)编号和其需要的时间,最后一行给出要查询的两个站点编号,对于每两个站点都可能存在多条通路,对于其中的每条通路由多条公交相连,其对应的路径中任意相邻两点为一条公交的起点和终点,这条公交的用时构成这条通路的一段坐车时间,这条通路的所有坐车时间的最大值和最小值的比值为该条路的最长坐车时间与最短坐车时间的比值,求查询的两个站点间的所有通路的这个比值中的最小值。

思路:将每条公交的起点和终点以及用时,组成一条边的起点和终点以及权值,利用贪心的思想,对这些边按权值从小到大排序,然后多次进行并查集的操作,一开始按顺序将所有边都加入,之后每次从前面去掉一条当前权值最小的边(这样最短坐车时间一定比上一次大,这样就能保证比值比上次的小)再操作,在将剩下的边重新相连,对于每次并查集的操作,在合并边的两个端点的同时用两个变量更新边的权值中的最大值和最小值(即:当前路径最大坐车时间和最短坐车时间),一旦查询的两点连通了(这是就是恰好连通的状态 ),就记录此时的比值并用其更新最小比值temp,这时break继续进行下一次并查集操作即可(因为后面的比值一定比当前恰好连通状态的比值大(边是经过排序的)),经过这样多次操作后便能找到恰好连通那两点的并且比值最小的那条,该比值即为答案。

完整代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=5500;
const int inf=0x3f3f3f;
int n,m,pre[maxn],zi,mu;
double temp=0x3f3f3f3f;
struct edge
{
    int s,t,w;
};
edge e[maxn];

int cmp(edge a,edge b)
{
    return a.w<b.w;
}

int gcd(int x,int y){return y==0?x:gcd(y,x%y);}

void init()
{
    for(int i=1;i<=n;i++)
        pre[i]=i;
}

int getRoot(int x)
{
    if(pre[x]==x){
        return x;
    }
    return pre[x]=getRoot(pre[x]);
}

void merge(int x,int y)
{
    x=getRoot(x);
    y=getRoot(y);
    if(x!=y){
        pre[x]=y;
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n>>m;
    for(int i=1;i<=m;i++) cin>>e[i].s>>e[i].t>>e[i].w;
    sort(e+1,e+m+1,cmp);
    int u,v;
    cin>>u>>v;
    for(int i=1;i<=m;i++){
        double maxx=-1,minx=inf;
        init();
        for(int j=i;j<=m;j++){//遍历去掉前i-1条边后剩下的边
            merge(e[j].s,e[j].t);
            maxx=max(maxx,(double)e[j].w);
            minx=min(minx,(double)e[j].w);
            if(getRoot(u)==getRoot(v)){
                if(maxx/minx<temp){
                    temp=maxx/minx;
                    zi=maxx;
                    mu=minx;
                }
                break;//这时break继续进行下一次并查集操作即可(因为后面的比值一定比当前恰好连通状态的比值大(边是经过排序的)
            }
        }
    }
    if(temp==0x3f3f3f3f){
        cout<<"IMPOSSIBLE"<<endl;
    }
    else{
        int g=gcd(zi,mu);
        zi/=g;
        mu/=g;
        if(mu==1){
            cout<<zi<<endl;
        }
        else{
            cout<<zi<<"/"<<mu<<endl;
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值