二分图

发布时间 2024-01-09 16:25:59作者: du463

二分图

什么是二分图
通俗的说就是将一个图能刚好分成两份就是二分图
感觉这里也需要用dfs来不断替换点

完全二分图

集合x和集合y每对顶点之间有且仅有一条边的图成为完全二分图

二分图判别方法

1.染色法
就是一条边对应的两个点必须是两种不同的颜色,并且整张图只可以有两种颜色

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
std::vector<int> g[N];
int c[N];

bool dfs(int x){
	for(auto y:g[x]){
		if(!c[y]){
			c[y]=3-c[x];
			if(!dfs(y)){
				return false;
			}
		}
		else{
			if(c[y]==c[x]){
				return false;
			}
		}
	}
	return true;
}

bool check(){
	memset(c,0,sizeof c);
	for(int i=1;i<=n;i++){
		if(!c[i]){
			c[i]=1;
			if(!dfs(i)){
				return false;
			}
		}
	}
	return true;

}
void solve(){
	//首先是建图,以无向图为例
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		g[x].push_back(y);
		g[y].push_back(x);
	}
	if(check()){
		cout<<"Yes"<<endl;
	}
	else{
		cout<<"No"<<endl;
	}
}

int main(){
	int t=1;
	while(t--){
		solve();
	}
	return 0;
}

二分图匹配

什么是二分图匹配问题?
就是将两个集合一个一个匹配,但是有可能一个集合的点对应另一个集合多个点,我们需要做的就是找到合适的边,使得完成最大匹配
完备匹配:二分图G中的最大匹配包含的边数=V1,无论再怎么加边都没办法再多,这就是完备匹配
交错路径
增广路径

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int n1,n2;
int v[N];
std::vector<int> g[N];
bool b[N];
bool find(int x){
	b[x]=true;
	for(auto y:g[x]){
		if(!v[y]||(!b[v[y]]&&find(v[y]))){
			v[y]=x;
			return true;
		}
	}
	return false;
	
}
int match(){
	int ans=0;
	memset(v,0,sizeof v);
	for(int i=1;i<=n1;i++){
		memset(b,false,sizeof b);
		if(find(i)){
			++ans;
		}
	}
	return ans;
}

void solve(){
	cin>>n>>m;
}
int main(){
	int t=1;
	while(t--){
		solve();
	}
	return 0;
}

棋盘覆盖问题

比如在n * n的棋盘上放置多米诺骨牌,其中m个各自已经放置了棋子,这些点不可以被覆盖,请你求出最多的放置骨牌的个数,也可以理解为骨牌最多可以覆盖多少个格子
思路:
先对空地方的棋盘进行染色,将图变成一个二分图,然后将其划分,最后求最大匹配就可以了
代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1010;

int n,m;
int a[50][50];
int n1,n2;
int r[50][50];
int v[820];
bool b[820];
int D[4][2]={{-1,0},{1,0},{0,1},{0,-1}};
std::vector<int> g[N];
bool find(int x){
	b[x]=true;
	for(auto y:g[x]){
		if(!v[y]||(!b[v[y]]&&find(v[y]))){
			v[y]=x;
			return true;
		}
	}
	return false;

}
int match(){
	int ans=0;
	memset(v,0,sizeof v);
	for(int i=1;i<=n1;i++){
		memset(b,false,sizeof b);
		if(find(i)){
			++ans;
		}
	}
}
void solve(){
	cin>>n>>m;
	memset(a,0,sizeof a);
	n1=n2=0;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		a[x][y]=1;//表示已经被占用了
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(!a[i][j]){
				if((i+j)&1){//判断行列之后是奇数还是偶数
					r[i][j]=++n2;
				}
				else{
					r[i][j]=++n1;
				}
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(!a[i][j]&&!((i+j)&1)){
				for(int k=0;k<4;k++){
					int x=i+D[k][0];
					int y=j+D[k][1];
					if(x<1||x>n||y<1||y>n||a[x][y]){
						continue;
					}
					g[r[i][j]].push_back(r[x][y]);
				}
			}
		}
	}
	cout<<2*match()<<endl;//放置了多少个牌就占了2倍的格子
	return ;


} 
int main(){
	int t=1;
	while(t--){
		solve();
	}
	return 0;

}

最大独立集

最小点覆盖