二分图(偶图)

定义

如果一个图G任意边的两个端点都分属于两个集合,则称图G为一个为二分图

匹配 

一个匹配即一个包含若干条边的集合,且其中任意两条边没有公共端点。

如图,红边即为一组匹配

最大匹配

在 G 的一个子图 M 中,M 的边集中的任意两条边都不依附于同一个顶点,则称 M 是一个匹配。
选择这样的边数最大的子集称为图的最大匹配问题,最大匹配的边数称为最大匹配数。如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。如果在左右两边加上源汇点后,图G等价于一个网络流,最大匹配问题可以转为最大流的问题。解决此问的匈牙利算法的本质就是寻找最大流的增广路径
 

染色法判定二分图

我们将相邻的两个点标记为不同的颜色,即每条边的两个顶点为不同的颜色,如果有冲突,则这个图不为二分图(实现多用D/BFS)

#include<bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
const int N = 2e5 + 5;
int n, m;//点数和边数 
int g[N];
int color[N];//1为红色,2为蓝色 
signed main(){
	cin >> n >> m;
	for(int u, v, i = 1; i <= m; i++){
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	//BFS 
	queue<int> q;
	q.push(1);
	color[1] = 1;//先将第一个点标为红色 
	while(q.size()){
		int u = q.front();
		for(int v : g[u]){
			if(color[v] == color[u]){//颜色冲突,不是二分图 
				puts("NO");
				return 0;
			}
			color[v] = 3 - color[u];//标记颜色 
			q.push(v);
		}
	}
	puts("YES");
	return 0;
}

匈牙利算法找最大匹配

洛谷 P3386【模板】二分图最大匹配

#include<bits/stdc++.h>
using namespace std;
const int N = 505;
vector<int> g[N];
int n, m, k, ans;
bool st[N];//判断这个点有没有被匹配 
int match[N];//储存这个点匹配的另一个点 
bool find(int u){
	for(int v : g[u]){
		if(!st[v]){//如果还没有被匹配 
			st[v] = 1;
			if(!match[v]/*还没有匹配*/|| find(match[v])/*已经和这个点匹配的另一个点可以换一个点匹配*/){
				match[v] = u;/*匹配*/ 
				return 1;
			}
		}
	}
	return 0;
}
int main(){
	cin >> n >> m >> k;
	for(int i = 0, u, v; i < k; i++){
		cin >> u >> v;
		g[u].push_back(v);
	}
	for(int i = 1; i <= n; i++){
		memset(st, 0, sizeof st);
		if(find(i)) ans++;//如果能匹配上 
	}	
	cout << ans;
	return 0;
}

洛谷 P1894 [USACO4.2] 完美的牛栏The Perfect Stall

//同上
#include<bits/stdc++.h>
using namespace std;
const int N = 205;
int n, m, ans;
vector<int> g[N];
bool st[N];
int match[N];
bool find(int u){
	for(int v : g[u]){
		if(!st[v]){
			st[v] = 1;
			if(!match[v] || find(match[v])){
				match[v] = u;
				return 1;
			}
		}
	}
	return 0;
}
int main(){
	cin >> n >> m;
	for(int i = 1, x, y; i <= n; i++){
		cin >> x;
		for(int j = 1; j <= x; j++){
			cin >> y;
			g[i].push_back(y);
		}
	}
	for(int i = 1; i <= n; i++){
		memset(st, 0, sizeof st);
		if(find(i)) ans++;
	}
	cout << ans;
	return 0;
}

洛谷 P1525 [NOIP 2010 提高组] 关押罪犯

先二分枚举答案,然后染色法判定,最后选出最优答案

#include<bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
const int N = 2e4 + 5;
int n, m;
vector<pii> g[N];
int color[N];
bool dfs(int u, int c, int limit){
	color[u] = c;
	for(pii t : g[u]){
		int v = t.first, w = t.second;
		if(w <= limit) continue;
		if(color[v]){
			if(color[v] == c) return 0;
		}
			
		else if(!dfs(v, 3 - c, limit)) return 0;
	}
	return 1;
}
bool check(int mid){
	memset(color, 0, sizeof color);
	for(int i = 1; i <= n; i++)
		if(!color[i]){
				if(!dfs(i, 1, mid))
				return 0;
		}
		
	return 1;
}
int main(){
	cin >> n >> m;
	for(int i = 1, u, v, w; i <= m; i++){
		cin >> u >> v >> w;
		g[u].push_back({v, w});
		g[v].push_back({u, w});
	}
	int l = 0, r = 1e9;
	while(l < r){
		int mid = l + r >> 1;
		if(check(mid)) r = mid;
		else l = mid + 1;
	}
	cout << l;
	return 0;
}

洛谷 P10937 車的放置

将行和列是为两个点,用行去匹配列,特判那些禁用的点

#include<bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
const int N = 205;
int n, m, t, ans;
int a[N][N];
bool st[N];
int match[N];
bool find(int x){
	for(int i = 1; i <= m; i++){
		if(!st[i] && !a[x][i]){
			st[i] = 1;
			if(!match[i] || find(match[i])){
				match[i] = x;
				return 1;
			}
		}
	}
	return 0;
}
int main(){
	cin >> n >> m >> t;
	for(int x, y, i = 0; i < t; i++){
		cin >> x >> y;
		a[x][y] = 1;
	}
	for(int i = 1; i <= n; i++){
		memset(st, 0, sizeof st);
		if(find(i)) ans++;
	}
	cout << ans;
	return 0;
}

洛谷 P10939 骑士放置

枚举每个点,用这个点去匹配其他骑士能到的八个点。

#include<bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
const int N = 105;
int n, m, t, ans;
int a[N][N];
bool st[N][N];
pii match[N][N];
int dir[8][2] = {1, 2, 2, 1, -1, -2, -2, -1, 1, -2, -1, 2, 2, -1, -2, 1};
bool find(int x, int y){
	for(int i = 0; i < 8; i++){
		int xx = x + dir[i][0];
		int yy = y + dir[i][1];
		if(xx < 1 || yy < 1 || xx > n || yy > m) continue;
		if(a[xx][yy] || st[xx][yy]) continue;
		st[xx][yy] = 1;
		pii t = match[xx][yy];
		if(t.first == 0 || find(t.first, t.second)){
			match[xx][yy] = {x, y};
			return 1;
		}
	}
	return 0;
}
int main(){
	cin >> n >> m >> t;
	for(int x, y, i = 0; i < t; i++){
		cin >> x >> y;
		a[x][y] = 1;
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			if(a[i][j] || (i + j) % 2) continue;
			memset(st, 0, sizeof st);
			if(find(i, j)) ans++;
		}
	}
	cout << n * m - ans - t;
	return 0;
}

洛谷 P10938 Vani和Cl2捉迷藏

传递闭包+匈牙利算法+有向图

#include<bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
const int N = 205;
int n, m, ans;
bool st[N];
int match[N];
int g[N][N];
bool find(int u){
	for(int v = 1; v <= n; v++){
		if(!g[u][v]) continue;
		if(!st[v]){
			st[v] = 1;
			if(!match[v] || find(match[v])){
				match[v] = u;
				return 1;
			}
		}
	}
	return 0;
}
int main(){
	cin >> n >> m;
	for(int i = 1, u, v; i <= m; i++){
		cin >> u >> v; 
		g[u][v] = 1;
	}
	for(int k = 1; k <= n; k++)
		for(int i = 1; i <= n; i++)
			for(int j = 1; j <= n; j++)
				g[i][j] |= g[i][k] & g[k][j];
	for(int i = 1; i <= n; i++){
		memset(st, 0, sizeof st);
		if(find(i)) ans++;
	}
	cout << n - ans;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值