(纪中)2418. Wormhole Sort【并查集】

(File IO): input:wormsort.in output:wormsort.out
时间限制: 1000 ms 空间限制: 262144 KB 具体限制
Goto ProblemSet


题目描述
F a r m e r J o h n Farmer John FarmerJohn 的奶牛们已经厌倦了他对她们每天早上排好序离开牛棚的要求。她们刚刚完成了量子物理学的博士学位,准备将这一过程搞快点。
今天早上,如同往常一样, F a r m e r J o h n Farmer John FarmerJohn N N N 头编号为 1 … N 的 奶 牛 1…N 的奶牛 1N(1≤N≤10^5)$,分散在牛棚中 N N N 个编号为 1 … N 1…N 1N 的不同位置,奶牛 i i i 位于位置 p i pi pi。但是今天早上还出现了 M M M 个编号为 1 … M 1…M 1M 的虫洞 ( 1 ≤ M ≤ 1 0 5 ) (1≤M≤10^5) 1M105,其中虫洞 i 双向连接了位置 a i ai ai b i bi bi,宽度为 w i ( 1 ≤ a i , b i ≤ N , a i ≠ b i , 1 ≤ w i ≤ 1 0 9 ) wi(1≤ai,bi≤N,ai≠bi,1≤wi≤10^9) wi1ai,biN,ai=bi,1wi109
在任何时刻,两头位于一个虫洞两端的奶牛可以选择通过虫洞交换位置。奶牛们需要反复进行这样的交换,直到对于 1 ≤ i ≤ N 1≤i≤N 1iN,奶牛 i i i 位于位置 i i i
奶牛们不想被虫洞挤坏。帮助她们最大化被她们用来排序的虫洞宽度的最小值。保证奶牛们有可能排好序。


输入
输入的第一行包含两个整数 N 和 M。
第二行包含 N N N 个整数 p 1 , p 2 , … , p N p1,p2,…,pN p1,p2,,pN。保证 p p p 1 … N 1…N 1N 的一个排列。
对于 1 1 1 M M M之间的每一个 i i i,第 i + 2 i+2 i+2 行包含整数 a i 、 b i ai、bi aibi w i wi wi

输出
输出一个整数,为在排序过程中奶牛必须挤进的虫洞的最小宽度的最大值。如果奶牛们不需要用任何虫洞来排序,输出 − 1 −1 1


样例输入
4 4
3 2 1 4
1 2 9
1 3 7
2 3 10
2 4 3

样例输出
9


数据范围限制
测试点 1 − 5 1-5 15 满足 N , M ≤ 1000 N,M≤1000 N,M1000
测试点 6 − 10 6-10 610 没有额外限制。


提示
​以下是一个仅用宽度至少为 9 9 9 的虫洞给奶牛排序的可能方案:
奶牛 1 1 1 和奶牛 2 2 2 使用第三个虫洞交换位置。
奶牛 1 1 1 和奶牛 3 3 3 使用第一个虫洞交换位置。
奶牛 2 2 2 和奶牛 3 3 3 使用第三个虫洞交换位置。


解题思路
题意就是说有 n n n个虫洞,在两个地方传送,现在问你让所有的奶牛都到自己的地方所穿过的虫洞宽度最小的最大值是多少。
抓住关键信息:
1.一个虫洞如果确定了要经过,那么就可以无限经过这个虫洞。
2.当确定一个虫洞要经过之后,宽度比他大的虫洞都可以经过。
本题要用到并查集和一点小小的贪心,每次加入一条最大的边,然后看看是否连通即可,因为本题数据范围较小,所以不需要启发式合并,具体做法详见代码注释。


代码

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;
int n,m,a[100010],s,f[100010],xx,yy,ans;
struct c{//x,y 为连接的两个节点,z为宽度
    int x,y,z;
}b[100010];
bool cmp(const c&l,const c&r)
{
    return l.z>r.z;
}
int find(int x)
{
    if(f[x]==x)
        return x;
    return f[x]=find(f[x]);
}//并查集之 find
int main(){
	freopen("wormsort.in","r",stdin);
	freopen("wormsort.out","w",stdout);
	scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&b[i].x,&b[i].y,&b[i].z);
    sort(b+1,b+m+1,cmp);
    s=0,ans=-1;
    for(int i=1;i<=n;i++) f[i]=i;  //初始化,每个节点最初的“根”是自己
    for(int i=1;i<=m;i++)
    {
        while(find(s)==find(a[s]))//判断是否连通 
        {
            s++;
            if(s>=n-1)
            {
                printf("%d",ans);
                return 0;
            }
        }
        xx=find(b[i].x);
        yy=find(b[i].y);
        if(xx!=yy)//若不连通,则加入这条边 
        {
            f[xx]=yy;
            ans=b[i].z;
        }
    }
    printf("%d",ans);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值