# 「NOIP2010」关押罪犯(二分图染色+二分答案)

「NOIP2010」关押罪犯(二分图染色+二分答案)

洛谷 P1525

  • 描述:n个罪犯(1-N),两个罪犯之间的仇恨值为c,m对仇恨值,求怎么分配使得两件监狱的最大仇恨值最小。

  • 思路:使最大xxx最小,描述就很二分。二分一个答案x后,对仇恨值大于x的罪犯之间构成的图进行二分图染色(相邻节点不染同一种颜色,总共两种颜色),染色成功则答案可行,复杂度\(O(nlogn)\)

二分图染色:把每个未标记的节点标记为任意一种颜色,对其进行一次 BFS,将该节点所在的连通分支全部染色,每一次扩展把未被染色的相邻节点标记为与自身相反的颜色,如果发现扩展出去的节点的颜色与自身相同,则染色失败。

AcCode:

#include <bits/stdc++.h>
using namespace std;
#define fre freopen("C:\\Users\\22765\\Desktop\\in.txt","r",stdin);
#define ms(a) memset((a),0,sizeof(a))
#define re(i,a,b) for(register int (i)=(a);(i)<(b);++(i))
#define sf(x) scanf("%d",&(x))
#define rg register
typedef long long LL;
const int inf=(0x7f7f7f7f);
const int maxn=1e5+5;
int n;
struct node{
	int x,y,r;
	bool operator < (const node& a)const{
		return r>a.r;
	}
}s[maxn]; 
vector<int> v[maxn];
int vis[maxn];
bool isok;

//dfs() : 能否将x节点所在的连通分支染色成功 
bool dfs(int x,int color){
	
	if(!isok)return 0;//该连通分支染色失败,则图无法染成二分图
	 
	vis[x]=color;
	int u;
	re(i,0,v[x].size()){
		u=v[x][i];
		if(vis[u]==vis[x]){
			isok=0;return 0;	
		}
		
		if(!vis[u]&&!dfs(u,3-color)){//若节点未被染色,且从该节点开始染色无法染色成功 
			isok=0;return 0; 
		}
	
	}
	return 1;
}
inline bool check(int m){
	ms(vis);isok=1;
	re(i,1,n+1)v[i].clear();
	
	re(i,0,m){
		if(s[i].r>m)//对仇恨值大于二分答案的节点建图 
			v[s[i].x].push_back(s[i].y),v[s[i].y].push_back(s[i].x);
		
		else break;
	}
	
	re(i,1,n+1){
		//这里一遍dfs可能无法将全部节点染色,可能有多个连通分支,所以遍历每个点 
		if(!vis[i]&&!dfs(i,1))return 0;
	}
	return 1;
}
int main(){
	sf(n);
	int m;sf(m);
	re(i,0,m)scanf("%d%d%d",&s[i].x,&s[i].y,&s[i].r);
	sort(s,s+m);//排序预处理便于后面建图 
//	cout<<s[0].r<<endl;
	int l=0,r=s[0].r,mid;
	while(l<r){
		mid=l+(r-l)/2;
		if(check(mid))r=mid;
		else l=mid+1;
	}
	cout<<l<<endl;
	return 0;
}

posted @ 2019-08-02 17:07  yhsmer  阅读(217)  评论(0编辑  收藏  举报