Living-Dream 系列笔记 第88期

二分图

定义:若可以将一张图分为两部分,且这两部分内部无连边,则称该图为二分图。

判定:无奇环。

判定证明

  • 必要性:若有奇环可以通过染色证明内部一定会有连边。

  • 充分性:若无环显然一定可以划分,若有偶环则也可以通过染色证明合法。

实现:交替染色,只要碰到了要染色的点已染色且颜色与当前节点相同就不可以划分,否则可以。

UVA10004

模板。

code
#include<bits/stdc++.h>
using namespace std;

const int N=2e2+5;
int n,m;
int color[N];
vector<int> G[N];

void init(){
	memset(color,0,sizeof color);
	for(int i=1;i<=n;i++)
		G[i].clear(); 
}
bool check(int cur,int c){
	color[cur]=c;
	for(int i:G[cur]){
		if(color[i]==c)
			return 0;
		else if(!color[i]){
			if(!check(i,3-c))
				return 0;
		}
	}
	return 1;
}

int main(){
	while(cin>>n&&n){
		init();
		cin>>m;
		for(int i=1,u,v;i<=m;i++){
			cin>>u>>v;
			u++,v++;
			G[u].push_back(v);
			G[v].push_back(u);
		}
		bool f=1;
		for(int i=1;i<=n;i++){
			if(color[i])
				continue;
			if(!check(i,1)){
				f=0; break;
			}
		}
		cout<<(f?"BICOLORABLE.\n":"NOT BICOLORABLE.\n");
	}
	return 0;
}

P1525

最大值最小想到二分。

两个监狱,考虑图论建模,自然想到二分图。

于是我们对于二分出的 \(x\),连边权 \(>x\) 的边表示排斥(即在不同监狱),这样同一监狱产生的冲突值都会 \(\le x\),判定其是否为二分图即可。

总结:挖掘题目中的提示,并考虑(特殊的)图论建模。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e5+5;
int n,m;
int color[N];
struct Node{
	int u,v,w;
}e[N];
vector<int> G[N];

void init(){
	memset(color,0,sizeof color);
	for(int i=1;i<=n;i++)
		G[i].clear();
}
bool ok(int cur,int c){
	color[cur]=c;
	for(int i:G[cur]){
		if(color[i]==c)
			return 0;
		else if(!color[i]){
			if(!ok(i,3-c))
				return 0;
		}
	}
	return 1;
}
bool check(int x){
	init();
	for(int i=1;i<=m;i++){
		if(e[i].w>x){
			G[e[i].u].push_back(e[i].v);
			G[e[i].v].push_back(e[i].u);
		}
	}
	bool f=1;
	for(int i=1;i<=n;i++){
		if(color[i])
			continue;
		if(!ok(i,1)){
			f=0; break;
		}
	}
	return f;
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n>>m;
	int mx=-1e9;
	for(int i=1,u,v,w;i<=m;i++){
		cin>>u>>v>>w;
		mx=max(mx,w),e[i]={u,v,w};
	}
	int l=-1,r=mx+1;
	while(l+1<r){
		int mid=(l+r)>>1;
		if(check(mid))
			r=mid;
		else
			l=mid;
	}
	cout<<r;
	return 0;
}

Hungary(饥饿)算法

解决二分图最大匹配问题。

称二分图中的一条边为匹配,则最大匹配是指这个图中最多不共点匹配个数。

显然我们可以用 dfs 无脑找,但显然这太 man 了。

于是我们可以对于每个点去找一个匹配,然后遇到匹配不上的点,就尝试让它想要匹配的点已经匹配的那个点换一个人进行匹配。这就是饥饿算法的本质,即找增广路径。

P3386

code
//
//  P3386.cpp
//  
//
//  Created by _XOFqwq on 2024/11/30.
//

#include <bits/stdc++.h>
using namespace std;

const int N=1e3+5;
int n,m,e;
int match[N];
bool vis[N];
vector<int> G[N];

bool dfs(int cur){
    if (vis[cur]) {
        return 0;
    }
    vis[cur]=1;
    for (int i : G[cur]) {
        if (!match[i]||dfs(match[i])) {
            match[i]=cur;
            return 1;
        }
    }
    return 0;
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>n>>m>>e;
    for (int i=1,u,v; i<=e; i++) {
        cin>>u>>v;
        G[u].push_back(v);
    }
    int ans=0;
    for (int i=1; i<=n; i++) {
        memset(vis,0,sizeof vis); //一次找到的不一定最优
        if (dfs(i)) {
            ans++;
        }
    }
    cout<<ans;
    return 0;
}

posted @ 2024-12-06 17:38  _KidA  阅读(20)  评论(0)    收藏  举报