并查集 —— P1525 关押罪犯

版权声明:转载请保留原地址 https://blog.csdn.net/u012972031/article/details/83693201

题目描述

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

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

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

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

题目地址:https://www.luogu.org/problemnew/show/P1525

思路:

  • 首先,因为要保证最大的冲突事件影响力最小,所以我们可以想到贪心+递推的思想,希望能保证两个监狱的目前最大影响值最小
  • 贪心的话,我们可以首先读取所有边(把两个罪犯看作两个点,他们之间的仇恨值看作一条边),并将他们由大到小排序,并使得一条边上的两个罪犯尽量不在同一个监狱。因为我们是贪心,所以可以证明:如果两个罪犯不得不进入一个监狱,那么由于当前已经进入监狱的罪犯们之间的仇恨值必定比当前仇恨值大,所以他们之间的这个仇恨值就是最大仇恨值。
  • 但是这样就有了一个问题:如何使得仇恨值大的罪犯们不在同一个监狱?首先,我们可以想到做两个集合,并使得一条边上的一个点放到集合A,另一个点放到集合B。但是,仅仅这样显然是不行的。这是因为放入集合A的那个点可能会与集合A中的某个点有仇恨,这就不满足递推的条件了(我们递推的条件是当前情况已经是最优)
  • 那么,我们就要保证这个即将放入某个集合的点和这个集合的其他点没有仇恨。但这是不能保证的,这个之后讨论。假设给的数据保证最后的最优解必定为0,也就是说每个监狱最终的总仇恨值都为0。
  • 那么这时候,我们在遍历边的时候,就可以不直接吧两个点分别加入两个监狱集合,而是首先保存两个点之间是仇人关系,如果两个点已经是仇人关系,就让他们分别进入两个监狱。由于递推和之前的条件,这样是没问题的。(这个假设不理解也没关系,因为我自己都不太理解
  • 接下来可以看这篇题解:https://www.luogu.org/blog/tanktt/solution-p1525 然后大概知道是怎么回事之后画一遍样例即可。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int cnt=0;
int n,m,fa[40005],b[40005];
struct Edge{
	int u,v,w;
}e[100005];
int cmp(Edge a,Edge b){
	return a.w>b.w;
}
void addEdge(int u,int v,int w){
	e[++cnt].u=u;
	e[cnt].v=v;
	e[cnt].w=w;
}
int find(int a){
	while(a!=fa[a]){
		a=fa[a]=fa[fa[a]];
	}
	return a;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		addEdge(a,b,c);
	}
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	sort(e+1,e+m+1,cmp);
	int ans=0;
	for(int i=1;i<=m+1;i++){
		int eu=e[i].u,ev=e[i].v;
		//cout<<eu<<" "<<ev<<endl;
		if(find(eu)==find(ev)){
			printf("%d\n",e[i].w);
			return 0;
		}
		if(!b[eu])b[eu]=ev;//如果点eu没有敌人的话,这次循环就只设置一下敌人
		else fa[find(b[eu])]=find(ev);//如果已经有敌人了,就将自己敌人放到和自己不同的阵营
		if(!b[ev])b[ev]=eu;
		else fa[find(b[ev])]=find(eu);
	}
	printf("0\n");
	return 0;
}

欢迎加入我们的OI讨论群

群号:849352599

猜你喜欢

转载自blog.csdn.net/u012972031/article/details/83693201