P1525 关押罪犯[扩展域并查集]

题目来源:洛谷

题目描述

S城现有两座监狱,一共关押着N名罪犯,编号分别为1N。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为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。

 

输入输出样例

输入样例#1: 
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884
输出样例#1: 
3512

说明

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

【数据范围】

对于30%的数据有N15。

对于70%的数据有N2000,M50000。

对于100%的数据有N20000,M100000。

 

解析:

这道题可以作为并查集扩展域的入门题目,在做过这道题后,我们会对扩展域有更深刻的理解。


【什么是扩展域】

鄙人薄见:扩展域用以维护较抽象的对象之间的逻辑关系,由于有点抽象,所以理解起来有点费劲。

扩展域将数据之间的关系分类,将对象之间不同的关系分开讨论,并在这些数据之间建立联系。

而且,这样的关系最好要具有传递性,这样某对象之间的关系就能相互导出。

这里难以理解的一点主要是,扩展域仅仅维护对象之间的关系,而不关心对象的其他特征

 

PS:目前为止,我只见到过维护二元关系的扩展域并查集。

 

就拿这一题举例吧,假设x和y在同一个监狱里,那么他们必然不会在不同的监狱里;如果x和y在不同的监狱里,那他们就不会在同一个监狱里。

听起来有点绕,我们分析一下:如果有一组关系表明x和y在同一个监狱,那么就可以推导出另一组关系x和y不会在不同的监狱。反之亦然。

 

于是我们可以给一个并查集划分几个不同的种类(或者叫域),用来存放对象之间不同的关系。

说说这道题我的解题思路吧:按照怨气值把较大的两个人分开,直到分不下去(两个罪犯无法避免在同一个监狱里)。

1.边权值大到小排序。
2.每次维护并查集:
x_self表示x所在监狱,x_another表示另1个监狱。
删一条边就把x_self和y_another合并,y_self和x_another合并。
前提条件:分不下去:二者在一个监狱(x_self=y_self||x_another=y_another)。

参考代码:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<cstring>
 6 #include<queue>
 7 #include<vector>
 8 #define N 100010
 9 using namespace std;
10 struct p{
11     int x,y,val;
12 }a[N];
13 bool operator<(p a,p b){
14     return a.val>b.val;
15 }
16 int head[N<<1],fa[N<<3],tot,n,m;
17 int get(int x)
18 {
19     if(fa[x]==x) return x;
20     return fa[x]=get(fa[x]);
21 }
22 void merge(int x,int y)
23 {
24     x=get(x),y=get(y);
25     if(x!=y) fa[x]=y;
26 }
27 int main()
28 {
29     scanf("%d%d",&n,&m);
30     for(int i=1;i<=2*n;i++) fa[i]=i;
31     for(int i=1;i<=m;i++){
32         scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].val);
33     }
34     sort(a+1,a+m+1);
35     int ans=0;
36     for(int i=1;i<=m;i++){
37         int x_self=a[i].x,x_another=a[i].x+n;
38         int y_self=a[i].y,y_another=a[i].y+n;
39         if(get(x_self)==get(y_self)||get(x_another)==get(y_another)){
40             ans=a[i].val;break;
41         }
42         merge(x_self,y_another);
43         merge(x_another,y_self);
44     }
45     cout<<ans<<endl;
46     return 0;
47 }

 

posted @ 2019-06-05 14:11  DarkValkyrie  阅读(282)  评论(0编辑  收藏  举报