二分图总结

匈牙利算法

bool dfs(int u)// 现在增广u这个点
{
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;// 枚举每一条与u连着的v
		if(!use[v])// v在此次增广中还没有使用过(如果v在此次增广中已经试过一次了就不用在ask他了)
		{
			use[v]=true;
			if(!link[v]||dfs(link[v]))// 如果v没有匹配,或者v有匹配但他的匹配可以匹配别的把v空出来
			{
				link[v]=u;
				return true;
			}
		}
	}
	return false;
}
int main()
{
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		memset(use,0,sizeof(use));//注意!
		if(dfs(i)) ans++;
	}
}

二分图基础题

luogu2071座位安排
https://www.luogu.org/problemnew/show/P2071
题目描述
已知车上有N排座位,有N*2个人参加省赛,每排座位只能坐两人,且每个人都有自己想坐的排数,问最多使多少人坐到自己想坐的位置。

输入输出格式
输入格式:

第一行,一个正整数N。
第二行至第N*2+1行,每行两个正整数Si1,Si2,为每个人想坐的排数。

输出格式:一个非负整数,为最多使得多少人满意。

输入输出样例
输入样例#1:
4
1 2
1 3
1 2
1 3
1 3
2 4
1 3
2 3

输出样例#1:
7

思路
  1. 因为每排座位最多坐两个人,所以一排座位对应着两个点,如果一个人想坐在这个座位,就往这个座位对应的两个点连边
  2. 然后匈牙利算法
  3. 注意!需要写邻接表
const int M=1e6+100;
const int N=1e5+10;
int link[4010];
bool use[4010];
int n;
struct node
{
	int v,nxt;
}edge[M];
int head[N],cnt;
void add(int u,int v)
{
	cnt++;
	edge[cnt].v=v;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}
bool dfs(int x)
{
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(!use[v])
		{
			use[v]=true;
			if(!link[v]||dfs(link[v]))
			{
				link[v]=x;
				return true;
			}
		}
	}
	return false;
}
int main()
{
	cin>>n;
	int a,b;
	for(int i=1;i<=n*2;i++)
	{
		cin>>a>>b;
		add(i,a);
		add(i,a+n);
		add(i,b);
		add(i,b+n);
	}
	int ans=0;
	for(int i=1;i<=n*2;i++)
	{
		memset(use,0,sizeof(use));
		if(dfs(i)) ans++;
	}
	cout<<ans<<endl;
	return 0;
}

二分图判定

  1. 方法黑白染色!
    在这里插入图片描述

Hall 定理

在这里插入图片描述

二分图匹配

1. 几个基本概念

最小点覆盖:选取最少的点,使任意一条边至少有一个端点被选择

最大独立集:选取最多的点,使任意所选两点均不相连

定理1:最大匹配数 = 最小点覆盖

定理2:最大独立集= 顶点数 - 最大匹配数

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read() {
	int x=0,f=1;char ch=' ';
	while(ch<'0'||ch>'9'){	if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){	x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=1e3+10;
int n,m;
int g[N][N],link[N]//与右边点匹配的左边点;
bool use[N];// 表示在此次寻找增广路时,右边的点访没访问过
bool dfs(int x)
{
	for(int i=1;i<=m;i++)
	{
		if(!use[i]&&g[x][i])
		{
			use[i]=true;
			if(!link[i]||dfs(link[i]))
			{
				link[i]=x;
				return true;
			}
		}
	}
	return false;
}
int main()
{
	int e;
	n=read();m=read();e=read();
	int i,j;
	for(i=1;i<=e;i++)
	{
		int u,v;
		u=read();v=read();
		//if(v>m||u>n) continue;
		g[u][v]=true;
	}
	int ans=0;
	memset(link,0,sizeof(link));
	for(i=1;i<=n;i++)
	{
		memset(use,0,sizeof(use));
		if(dfs(i)) ans++;
	}
	cout<<ans<<endl;
	return 0;
}

例题

1) bzoj1741: [Usaco2005 nov]Asteroids 穿越小行星群

贝茜想驾驶她的飞船穿过危险的小行星群.小行星群是一个NxN的网格(1≤N≤500),在网格内有K个小行星(1≤K≤10000). 幸运地是贝茜有一个很强大的武器,一次可以消除所有在一行或一列中的小行星,这种武器很贵,所以她希望尽量地少用.给出所有的小行星的位置,算出贝茜最少需要多少次射击就能消除所有的小行星.
第1行:两个整数N和K,用一个空格隔开.
第2行至K+1行:每一行有两个空格隔开的整数R,C(1≤R,C≤N),分别表示小行星所在的行和列.
一个整数表示贝茜需要的最少射击次数,可以消除所有的小行星
in
3 4
1 1
1 3
2 2
3 2

out
2

思路

  1. 摧毁一个小行星,既可以从行也可以从列。但是行和列中必须选一个摧毁
  2. 所以我们从行连一条边到列,发现这个二分图的摧毁最小覆盖集上的点就可以摧毁所有小行星,又最小覆盖集=最大匹配数。
  3. 所以建图跑二分图匹配

code

注意!!! link有可能是关键字!!!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
inline int read(){
	char ch=' ';int f=1;int x=0;
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
const int N=510;
int link[N],use[N]; 
int n,m;
int g[N][N];
bool dfs(int x)
{
	for(int i=1;i<=n;i++)
	{
		if(!use[i]&&g[x][i])
		{
			use[i]=true;
			if(!link[i]||dfs(link[i]))
			{
				link[i]=x;
				return true;
			}
		}
	}
	return false;
}
int main()
{
	n=read();m=read();
	int i,j;
	for(i=1;i<=m;i++)
	{
		int a=read(),b=read();
		g[a][b]=1;
	}
	int ans=0;
	for(i=1;i<=n;i++)
	{
		memset(use,0,sizeof(use));
		if(dfs(i)) ans++;
	}
	cout<<ans<<endl;
	return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值