二分图,是指这样一种图:如果有一条边链接了点 ,那么可以把原图所有的点分成两个集合 ,使得 分属 两个集合。
所谓二分图染色,其实是一种二分图的判定方法。它的基本思想是把每个点染成黑、白两种颜色,看看是否可以让每条边的两个点的颜色不一样。
二分图染色的基本步骤如下:
选择一个起始点
,把
染成黑白任意一种颜色,完成则转
;
假设现在遍历到点
,循环
的每个相邻点
,转
;
如果
没有染色,则染与
不同的颜色,遍历点
,转
;否则如果
的颜色与
的颜色相同,判定失败;否则继续找下一个
,转
。
代码如下:
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);
}
并查集是维护联通关系的一个利器,它的基本思想是找祖宗。
一开始的时候,每个点的祖宗记为自己(记为 )。如果发现 有祖宗与后代的关系,则 (当然, 也可以)。判断 是否为 的祖宗时,则只需判断顺着 数组爬就可以了。
并查集有两个非常常见的优化:一是路径优化(即爸爸的爸爸是我的爸爸),二是按秩合并(即小的合并到大的上)。
代码:
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;
【题目链接】: https://www.luogu.com.cn/problem/P1525
【思路】: 二分答案,记最小的冲突事件的影响力为 ,那么影响力 的两个人要拆开。这就可以用二分图染色法判断。
【代码】:
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;
}