2020.01.05日常总结兼并查集、二分图染色略讲

二分图染色

二分图,是指这样一种图:如果有一条边链接了点 ( a , b ) (a,b) ,那么可以把原图所有的点分成两个集合 X , Y X,Y ,使得 a , b a,b 分属 X , Y X,Y 两个集合。

所谓二分图染色,其实是一种二分图的判定方法。它的基本思想是把每个点染成黑、白两种颜色,看看是否可以让每条边的两个点的颜色不一样。

二分图染色的基本步骤如下:

( 1 ) (1) 选择一个起始点 u u ,把 u u 染成黑白任意一种颜色,完成则转 ( 2 ) (2)
( 2 ) (2) 假设现在遍历到点 v v ,循环 v v 的每个相邻点 t o to ,转 ( 3 ) (3)
( 3 ) (3) 如果 t o to 没有染色,则染与 v v 不同的颜色,遍历点 t o to ,转 ( 2 ) (2) ;否则如果 t o to 的颜色与 v v 的颜色相同,判定失败;否则继续找下一个 t o to ,转 ( 3 ) (3)

代码如下:

int color[N];
//color[i]:
//0:不确定
//1:染黑色
//2:染白色
bool dfs(int u){
	for(int i=h[u];i;i=e[i].next){
		register int to=e[i].to;
		if (color[to]==color[u])
			return false;//相同颜色,判定失败
		else if (!color[to]){
			color[to]=3-color[u];
			if (!dfs(to))
				return false;
		}
	}
	return true;
}
bool check(int u){
	memset(color,0,sizeof(color));
	color[u]=1;//染什么颜色都可以
	return dfs(u);
}

并查集

并查集是维护联通关系的一个利器,它的基本思想是找祖宗。

一开始的时候,每个点的祖宗记为自己(记为 f i f_i )。如果发现 a , b a,b 有祖宗与后代的关系,则 f b = a f_b=a (当然, f a = b f_a=b 也可以)。判断 a a 是否为 b b 的祖宗时,则只需判断顺着 f f 数组爬就可以了。

并查集有两个非常常见的优化:一是路径优化(即爸爸的爸爸是我的爸爸),二是按秩合并(即小的合并到大的上)。

代码:

struct union_set{
//	union_set:并查集
	int f[N],s[N];
	void init_set(int n){
		memset(f,0,sizeof(f));
		memset(s,0,sizeof(s));
		for(int i=1;i<=n;i++){
			f[i]=i;s[i]=1;
		}
	}
	int getf(int x){
		if (f[x]==x) return x;
		return f[x]=getf(f[x]);
	}
	void merge(int a,int b){
		int x=getf(a),y=getf(b);
		if (s[x]<s[y]) swap(x,y);
		if (x==y) return;
		f[y]=x;s[x]+=s[y];
	}
	bool query(int a,int b){
		return getf(a)==getf(b);
	}
}F;

P 1525 例题——洛谷P1525

【题目链接】: https://www.luogu.com.cn/problem/P1525

【思路】: 二分答案,记最小的冲突事件的影响力为 m i d mid ,那么影响力 > m i d >mid 的两个人要拆开。这就可以用二分图染色法判断。

【代码】:

const int M=100100;
const int N=20010;
struct node{
	int next,to;
}e[M<<1];int h[N],tot;
inline int add(int a,int b){
	e[++tot]=(node){h[a],b};h[a]=tot;
}
int color[N];
//color[i]=
//0:i的颜色未确定
//1:i的颜色为白
//2:i的颜色为黑
bool dfs(int u){
	for(int i=h[u];i;i=e[i].next){
		register int to=e[i].to;
		if (color[to]==0){
			color[to]=3-color[u];
			if (!dfs(to))
				return false;
		}
		else if (color[to]==color[u])
			return false;
	}
	return true;
}
//非常标准的二分图判定代码
int n,m,L[M],R[M],P[M];
inline void build(int mid){
	memset(h,0,sizeof(h));tot=0;
	for(int i=1;i<=m;i++)
	if (P[i]>mid){
		add(L[i],R[i]);
		add(R[i],L[i]);
	}
}
inline bool check(int mid){
	build(mid);
	memset(color,0,sizeof(color));
	for(int i=1;i<=n;i++)
	if (!color[i]){
		color[i]=1;
		if (!dfs(i))
			return false;
	}
	return true;
}
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
	int x=0;bool f=0;char c=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
int l,r,mid,ans,i;
int main(){
	n=read();m=read();
	for(i=1;i<=m;i++){
		L[i]=read();
		R[i]=read();
		P[i]=read();
		r=max(r,P[i]);
	}
	while (l<=r){
		mid=(l+r)>>1;
		if (check(mid)){
			ans=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	printf("%d",ans);
	return 0;
}
发布了82 篇原创文章 · 获赞 4 · 访问量 1766

猜你喜欢

转载自blog.csdn.net/ZHUYINGYE_123456/article/details/103842246