题目描述

S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为c 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为c 的冲突事件。

每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到S 城Z 市长那里。公务繁忙的Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。

在详细考察了 N 名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。那么,应如何分配罪犯,才能使 Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?

输入描述:

		
第一行为两个正整数N和M,分别表示罪犯的数目以及存在仇恨的罪犯对数。 
接下来的M行每行为三个正整数aj,bj,cj,表示aj号和bj号罪犯之间存在仇恨,其怨气值为cj
数据保证1≤aj<bj≤N,0<cj≤1,000,000,000,且每对罪犯组合只出现一次。

输出描述:

共1行,为Z市长看到的那个冲突事件的影响力。如果本年内监狱中未发生任何冲突事件,请输出0。

思路

排序+遍历+并查集
由于市长只看最大值,所以先对输入数据按照影响力从大到小排序。
然后遍历,可以发现:如果在某一时候,罪犯a与b出现,并且find(a)==find(b),那么此时的v一定是最小值了。
如果find(a)始终不等于find(b),那么输出0
至于后面如何判断,这里可以看一楼大佬的思路,即:如果a与b分开,b与c分开,那么a与c一定合并。
所以可以开一个数组记录与i敌对的敌人编号
前提:find(a)!=find(b)
如果a目前没有敌对的人,那么现在有了(就是b
如果a已经有了敌对的人,那么这个敌对的人就是b的室友(即merge(a,enemy[b]))
对b同理
但是这题也可以用带权并查集写出来,只要把带权并查集的权值变成关系就行了,看下面第二个代码(注意取模)
开一个value表示关系数组

再开一个数组的代码如下(直接都开long long int就行,反正不会MLE):
#include<bits/stdc++.h>
#define ll long long int
using namespace std;
struct node{
    ll a,b,v;
}da[100020];
ll father[100020],enemy[100020];
int cmp(struct node a,struct node b){
    return a.v>b.v;
}
ll find(ll x){
    if(father[x]==x) return x;
    else return father[x]=find(father[x]);
}
void merge(ll a,ll b){
    father[find(a)]=find(b);
}
int main(){
    ll N,M;
    scanf("%lld%lld",&N,&M);
    for(ll i=1;i<=N;i++) father[i]=i;
    for(ll i=1;i<=M;i++){
        scanf("%lld%lld%lld",&da[i].a,&da[i].b,&da[i].v);
    }
    sort(da+1,da+1+M,cmp);
    for(ll i=1;i<=M;i++){
        if(find(da[i].a)==find(da[i].b)){
            printf("%lld\n",da[i].v);
            return 0;
        }
        if(!enemy[da[i].a]){
            enemy[da[i].a]=da[i].b;
        }else{
            merge(enemy[da[i].a],da[i].b);
        }
        if(!enemy[da[i].b]){
            enemy[da[i].b]=da[i].a;
        }else{
            merge(enemy[da[i].b],da[i].a);
        }
    }
    printf("0\n");
    return 0;
}

带权并查集代码如下:
#include<bits/stdc++.h>
#define ll long long int
using namespace std;
struct node{
    ll a,b,v;
}da[100020];
ll father[100020],value[100020]; //value表示关系数组,0表示室友,1表示非室友
int cmp(struct node a,struct node b){
    return a.v>b.v;
}
ll find(ll x) {
    if(x!=father[x]) {
        ll t=father[x];
        father[x]=find(father[x]);
        value[x]=(value[x]+value[t])%2; //注意取模
    }
    return father[x];
}
int merge(ll a,ll b,ll val) {
    ll pa=find(a),pb=find(b);
    if(pa!=pb) { 
        father[pa]=pb;
        value[pa]=(-value[a]+value[b]+val)%2; //注意取模
    }else{
        if(value[a]==value[b]) return 0;  //表示a和b处于同一个集合内(两种情况:1.value[a]=value[b]=0,即室友的室友是室友;2.value[a]=value[b]=1,即非室友的非室友还是室友
    }
    return 1; //表示a和b不处于同一个集合
}
int main(){
    ll N,M;
    scanf("%lld%lld",&N,&M);
    for(ll i=1;i<=N;i++) father[i]=i;
    for(ll i=1;i<=M;i++){
        scanf("%lld%lld%lld",&da[i].a,&da[i].b,&da[i].v);
    }
    sort(da+1,da+1+M,cmp);
    for(ll i=1;i<=M;i++){
        if(!merge(da[i].a,da[i].b,1)){
            printf("%lld\n",da[i].v);
            return 0;
        }
    }
    printf("0\n");
    return 0;
}