二分+判断二分图或并查集 关押罪犯(洛谷P1525)

关押罪犯

题目描述

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<aj≤bj≤N,0<cj≤1,000,000,000,且每对罪犯组合只出现一次。

输出格式

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


两种解法;

一、并查集+贪心

这道题如果能想到用并查集,那真的是牛逼;

首先我们知道,我们要尽量保证怨气值大的一对犯人不在一个监狱,所以我们可以先对这些的犯人的怨气值排个序,先把怨气值大的分开,怎么分呢?这是并查集的作用就体现了,我们可以把一个人的敌人都放在一个集合里,当两个人的敌人都一样时,这时已经不可分了,这两个人的怨气值就是答案了;

代码:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define lson k<<1
#define rson k<<1|1
//ios::sync_with_stdio(false);
using namespace std;
const int N=100010;
const int M=200100;
const LL mod=1e9+7;
struct Node{
	int x,y,w;
}edge[N];
int fa[20100];
int a[20100];//仇人
int find(int p){
	if(fa[p]==p) return p;
	return fa[p]=find(fa[p]);
}
bool cmp(Node p,Node q){
	return p.w>q.w;
}
int main(){
	ios::sync_with_stdio(false);
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++) cin>>edge[i].x>>edge[i].y>>edge[i].w;
	sort(edge+1,edge+1+m,cmp);
	int ok=1;
	for(int i=1;i<=m;i++){
		if(find(edge[i].x)==find(edge[i].y)){
			cout<<edge[i].w<<endl;
			ok=0;
			break;
		}
		else{
			if(!a[edge[i].x]) a[edge[i].x]=edge[i].y;
			else fa[find(edge[i].y)]=find(a[edge[i].x]);
			if(!a[edge[i].y]) a[edge[i].y]=edge[i].x;
			else fa[find(edge[i].x)]=find(a[edge[i].y]);
		}
	}
	if(ok) cout<<0<<endl;
	return 0;
}

二、二分答案+染色法判断二分图

这个比较好想,但是实现起来比较麻烦;

首先,我们知道如果罪犯之间的关系如果是一个二分图,那么他们之间就不会有冲突,所以我们要做的就是在去掉一些边之后,看剩下的边是否是一个二分图;然后我们发现,去边是单调性的,可以二分;

代码:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define lson k<<1
#define rson k<<1|1
//ios::sync_with_stdio(false);
using namespace std;
const int N=100100;
const int M=100100;
const LL mod=1e9+7; 
struct Node{
	int to,nex,w;
}edge[N<<1];
int head[20100];
int cnt;
int s[N];
int n,m;
void add(int p,int q,int w){
	edge[cnt].w=w;
	edge[cnt].to=q;
	edge[cnt].nex=head[p];
	head[p]=cnt++;
}
int color[20100];
bool dfs(int p,int q,int d){
	color[p]=q;
	for(int i=head[p];~i;i=edge[i].nex){
		if(edge[i].w<=s[d]) continue;
		int v=edge[i].to;
		if(!color[v]){
			if(!dfs(v,3-q,d)) return false;
		}
		else if(color[v]==q) return false;
	}
	return true;
}
bool judge(int d){
	memset(color,0,sizeof(color));
	for(int i=1;i<=n;i++){
		if(!color[i]){
			if(!dfs(i,1,d)) return false;
		}
	}
	return true;
}
int main(){
	ios::sync_with_stdio(false);
	memset(head,-1,sizeof(head));
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int p,q,w;
		cin>>p>>q>>w;
		add(p,q,w);
		add(q,p,w);
		s[i]=w;
	}
	sort(s+1,s+m+1);//二分数组下标 
	int l=0,r=m;
	int mmin=0;
	while(l<=r){
		int d=(l+r)>>1;
		if(judge(d)){
			mmin=d;
			r=d-1;
		}
		else l=d+1;
	}
	cout<<s[mmin]<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值