【题解】[NOIP2010]关押罪犯

题目

题目描述

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

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

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

那么,应如何分配罪犯,才能使 Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?

输入格式

每行中两个数之间用一个空格隔开。第一行为两个正整数 N , M N,M ,分别表示罪犯的数目以及存在仇恨的罪犯对数。接下来的 M M 行每行为三个正 a j , b j , c j a_j,b_j,c_j ,表示 a j a_j 号和 b j b_j 号罪犯之间存在仇恨,其怨气值为 c j c_j 。数据保证 1 < a j b j N , 0 < c j 1 0 9 1<a_j\leq b_j\leq N, 0 < c_j\leq 10^9 ,且每对罪犯组合只出现一次。

输出格式

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

输入输出样例

输入 #1

4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884

输出 #1

3512

说明/提示

【输入输出样例说明】
罪犯之间的怨气值如下面左图所示,右图所示为罪犯的分配方法,市长看到的冲突事件影响力是 3512 3512 (由 2 2 号和 3 3 号罪犯引发)。其他任何分法都不会比这个分法更优。
在这里插入图片描述
【数据范围】

对于 30 % 30\% 的数据有 N 15 N\leq 15

对于 70 % 70\% 的数据有 N 2000 , M 50000 N\leq 2000,M\leq 50000

对于 100 % 100\% 的数据有 N 20000 , M 100000 N\leq 20000,M\leq 100000

题解

思路

首先我们需要尽可能解决怨气值大的矛盾,所以对矛盾按怨气值从大到小排序。

接下来我们考虑一个一个解决矛盾,就是对罪犯分组,如果现在要考虑的这个矛盾的两个罪犯已经在同一组了,那么就解决不了这个矛盾了,答案就是这个怨气值。

否则就可以解决这个矛盾,那就需要设置对立集合,可以用一个数组 f f 表示每个罪犯它的对立集合是哪个罪犯所在的集合。

对于这一次的两个罪犯 A A B B ,如果 A A 没有对立集合,就把 f [ A ] f[A] 设为 B B ,如果有对立集合,就把 f [ A ] f[A] 所在的集合和 B B 所在的集合合并。如果 B B 没有对立集合,就把 f [ B ] f[B] 设为 A A ,如果有对立集合,就把 f [ B ] f[B] 所在的集合和 A A 所在的集合合并。

如果一直都没有解决不了的矛盾,答案就是 0 0

代码

具体怎么实现看代码

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int fa[20005],f[20005];
int n,m,ans;
struct edge{
	int a;
	int b;
	int c;
};
bool cmp(edge a,edge b){//按照怨气值从大到小排序
	if(a.c!=b.c){
		return a.c>b.c;
	}
	return a.a<b.a;
}
edge e[100005];
void init(){
    for(int i=1;i<=n;i++){
        fa[i]=i;
    }
}
int get(int x){
    if(fa[x]==x){
        return x;
    }
    return fa[x]=get(fa[x]);
}
void merge(int x,int y){
	x=get(x);
	y=get(y);
	if(x!=y){
		fa[y]=x;
	}
} 
int main() {
	scanf("%d %d",&n,&m);
	for(int i=0;i<m;i++){
		scanf("%d %d %d",&e[i].a,&e[i].b,&e[i].c);
	}
    init();
    sort(e,e+m,cmp);
    for(int i=0;i<m;i++){
        if(get(e[i].a)==get(e[i].b)){//如果这个矛盾关系的双方已经在同一个集合里了,那么这个怨气值就是答案,并且不用再看后边的循环了。
            ans=e[i].c;
            break;
        }
        else{//否则的话,
            if(f[e[i].a]){//先看f[e[i].a]是不是非0,是的话就是已经有对立集合了,就把f[e[i].a]所在的集合和e[i].b所在的集合合并,
                merge(f[e[i].a],e[i].b);
            }
			else{//否则就是没有对立集合,就把f[e[i].a]赋值为e[i].b。
                f[e[i].a]=e[i].b;
            }
            if(f[e[i].b]){//再看f[e[i].b]是不是非0,是的话说明e[i].b有对立集合,就把f[e[i].b]所在的集合和e[i].a所在的集合合并,
                merge(f[e[i].b],e[i].a);
            }
			else{//否则就是没有对立集合,就把f[e[i].b]赋值为e[i].a。
                f[e[i].b]=e[i].a;
            }
        }
    }
    printf("%d",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/tanfuwen_/article/details/107920559