首页 > 其他分享 >二分图

二分图

时间:2023-07-19 22:33:24浏览次数:27  
标签:二分 匹配 int st 集合 match

一. 定义

二分图是节点由两个集合组成,且两个集合内部没有边的图。

换言之,存在一种方案,将节点划分成满足以上性质的两个集合。


比如下图就是一个二分图,两个集合的元素可以用两种颜色表示,每条边上连接的点属于不同的集合,相同集合的两个点上没有边

注意:二分图中不存在元素为奇数的环


二 . 二分图的判定(染色法)

我们可以枚举每个点,如果点还没被染色,那么将其染为1并用dfs遍历这个点所在的连通块的每个点,染1染2这样交替进行,一旦发现冲突(两个相邻的点是同一个颜色)那么该图就不是一个二分图


例题 二分图判定板子

给你一张简单无向图,你需要判断这张图是否为二分图。

图用以下形式给出:

第一行输入两个整数 n,m,表示图的顶点数和边数,顶点编号从 1 到 n。

接下来 m 行,每行两个整数 x,y,表示 x 和 y 之间有一条边。

输出一个字符串 Yes 或者 No,Yes 表示是二分图。

输入格式
第一行两个整数 n,m。

接下来 m 行,每行有两个整数,代表一条边。

输出格式
输出一个字符串表示答案。

样例输入
4 4
1 2
2 3
3 4
1 4
样例输出
Yes
数据规模
对于所有数据,保证 2≤n≤1000,0≤m≤10000,1≤x,y≤n,x≠y

代码

# include<bits/stdc++.h>
 
using namespace std;
 
const int N = 1e3+10;
int n,m;
int st[N];	//染色,没染为0,染色为1或2
 
vector<int> edge[N];
 
bool dfs(int x)	//返回值为染色过程中是否会发生冲突
{
	for(auto t: edge[x]) 
	{
		if(!st[t]) 
		{
			st[t] = 3-st[x];	//将1变成2,2变成1
			if(!dfs(t)) return false;	//如果染色过程中发生冲突,返回false
		}
		else if(st[t] == st[x]) return false;	//如果两个临点颜色一样,返回false
 	}
 	return true;
}
int main()
{
	cin>>n>>m;
	for(int i=0;i<m;i++) 
	{
		int x,y;scanf("%d%d",&x,&y);
		edge[x].push_back(y);
		edge[y].push_back(x);
	}
	for(int i=1;i<=n;i++)
	{
		if(!st[i])		//遍历每个点,只要这个点没被染色,标1
		{
			st[i] = 1;
			if(!dfs(i)) //这里的dfs是将该点所在连通块的所有点染色
			{
				cout<<"No\n";
				return 0;
			}
		}
	}
	cout<<"Yes\n";
	return 0;
}

三 . 二分图的最大匹配(匈牙利算法)

求二分图的最大匹配用到的是匈牙利算法

匹配的意思是每一个点都只对应着另一个集合的一个点,不存在一对多,多对一的现象

匈牙利算法的流程是 从二分图的两个集合中选择一个集合遍历集合中每一个点 如果这个点与之连线的点还没有被匹配,则与之匹配。或者即使有匹配但与之匹配的点还能在找到与之匹配的,那么贪心的将能匹配的都匹配上


例题1 匈牙利算法板子题(acwing 861)

给定一个二分图,其中左半部包含 n1 个点(编号 1∼n1),右半部包含 n2 个点(编号 1∼n2),二分图共包含 m 条边。

数据保证任意一条边的两个端点都不可能在同一部分中。

请你求出二分图的最大匹配数。

二分图的匹配:给定一个二分图 G,在 G 的一个子图 M 中,M 的边集 {E} 中的任意两条边都不依附于同一个顶点,则称 M 是一个匹配。

二分图的最大匹配:所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数。

输入格式
第一行包含三个整数 n1、 n2 和 m。

接下来 m 行,每行包含两个整数 u 和 v,表示左半部点集中的点 u 和右半部点集中的点 v 之间存在一条边。

输出格式
输出一个整数,表示二分图的最大匹配数。

数据范围
1≤n1,n2≤500
1≤u≤n1
1≤v≤n2
1≤m≤105

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

代码

# include<bits/stdc++.h>
 
using namespace std;
 
int n1,n2,m;
 
const int N = 510;
vector<int> edge[N];    //存边
int st[N],match[N];     //st存这轮是否被访问过,match存每个点的匹配是谁
 
bool find(int x)        //返回这个点是否能找到匹配
{
    for(auto i:edge[x]) //遍历这个点所有与之相邻的点
    {
        if(!st[i])      //如果st[i]则证明这轮已经找过这个点了,这个点已经进行过调整了,再进行调整不会有任何变化
        {
            st[i] = true;
            if(match[i] == 0 || find(match[i]) ) //如果还没有被匹配或者匹配的点还能找到新的匹配 则x点与i匹配成功
            {
                match[i] = x;
                return true;
            }
        }
    }
    return false;
}
int main()
{
	cin>>n1>>n2>>m;
	for(int i=0;i<m;i++)
	{
	    int u,v;
	    cin>>u>>v;
	    edge[u].push_back(v);   //因为每次都是从n1这个集合里的点访问边,所以需要设成有向图,由n1的点指向n2的点
	}
	int ans = 0;
	for(int i=1;i<=n1;i++)
	{
	    memset(st,0,sizeof st); //这个st只代表找与i匹配的点的时候每个数字有没有被遍历过,每轮st都要置0
	    if(find(i)) ans++;      //如果这个点找到一个匹配,ans++
	}
	cout<<ans<<endl;
	return 0;
}

例题2 代码源oj793 棋盘覆盖问题

现在有一个 n 行 n 列的棋盘,坐标范围从 (1,1)−(n,n),你需要用 1×2 的多米诺骨牌去覆盖这张棋盘。

这张棋盘上有 m 个格子已经放置了棋子,即这些格子不能被覆盖,现在要你求出骨牌最多可以覆盖多少个格子。

第一行输入 n,m。

接下来 m 行,每行两个数字 xi,yi 表示棋子的坐标。

输出一个数表示最多覆盖的格子数。

输入格式
第一行两个整数 n,m。

接下来 m 行,每行有两个整数。

输出格式
输出一个数字表示答案。

样例输入
3 1
2 2
样例输出
8
数据规模
对于所有数据,保证 2≤n≤40,0≤m≤n2,1≤xi,yi≤n,棋子所在格子各不相同。


我们可以发现除了已经给出的 不可被覆盖的点外,每个多米诺骨牌需要占两个点,

那么我们其实可以将每相邻的两个点标记为不同的颜色,且一共只用两种颜色标记点。那么这个图的点就会形成颜色不同的两个集合,也就转化成了二分图

且每个相邻的两个点之间都连有一条边。选取最大的覆盖面积也就从选取最多的相邻两点转变成了二分图的最大匹配。

代码

# include<bits/stdc++.h>
 
using namespace std;
 
const int N = 2500;
 
int n,m;
int st[N][N];	//每个点的颜色
int n1,n2;		//左边集合有几个点,右边集合有几个点
int c[N][N];		//这个点是左边或者右边的第几个点(是左是右由st判断)
int match[N] ;   //右边的这个点匹配的是左边的哪个点	
vector<int> edge[N];
int dx[4] = {-1,0,1,0},dy[4] = {0,1,0,-1};
bool d[N]; //匈牙利算法中判断这个点是否被遍历过
 
bool find(int x)
{
	for(auto c : edge[x])
	{
		if(!d[c]) 
		{
			d[c] = true;
			if(match[c] ==0 || find(match[c])) 
			{
				match[c] = x;
				return true;
			}
		}
	}
	return false;
}
 
int main()
{
	cin>>n>>m;
	for(int i=0;i<m;i++)
	{
		int x,y;scanf("%d%d",&x,&y);
		st[x][y] = 1;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			if(st[i][j] == 1) continue;		//如果这个点是不能被覆盖的点那么直接continue
			if((i+j)%2 == 0)  				//我们可以定义染1号颜色的与原点的曼哈顿距离为偶数那么2号颜色就为奇数
			{
				st[i][j] = 2; n1++;		
				c[i][j] = n1;
			}
			else 
			{
				n2++;
				c[i][j] = n2;
				
			}
		}
 
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(st[i][j] == 2) 
			{
				for(int k=0;k<4;k++)
				{
					int x = i+dx[k],y = j+dy[k];
					if(x<1 || x>n || y<1 || y>n || st[x][y] == 1) continue;	
					edge[c[i][j]].push_back(c[x][y]);	//将每个右边集合的元素加入到与之相邻左边集合的元素中
				}
			}
		}
	}
	int ans = 0;
	for(int i=1;i<=n1;i++)	//然后就是匈牙利算法的板子
	{
		memset(d,0,sizeof d);
		if(find(i)) ans++;
	}
	cout<<ans*2<<endl;
 	return 0;
}

例题3 代码源oj799 最大独立集

给你一张二分图,图中没有重边,你需要求出这张图中最大独立集包含的顶点个数。

最大独立集是指:在图中选出最多的点,满足他们两两之间没有边相连。

图用以下形式给出:

第一行输入两个整数 n,m,表示图的顶点数和边数,顶点编号从 1 到 n。

接下来 m 行,每行两个整数 x,y,表示

标签:二分,匹配,int,st,集合,match
From: https://www.cnblogs.com/algoshimo/p/17566959.html

相关文章

  • 2023夏季集训D1-贪心二分
    2023夏季集训D1贪心二分0x00前言24OIFXJ大佬来给我们讲课OrzOrz.讲课好难TAT.0x10贪心0x11经典贪心写了BestCowLineG/S和田忌赛马一位小伙从同学那里要来了一份BestCow代码Debug但没有发现代码没有输入,这是他思路3小时后发生的hack.田忌赛马太......
  • LeetCode 1201. Ugly Number III 数学+二分答案
    Anuglynumberisapositiveintegerthatisdivisibleby\(a\),\(b\),or\(c\).Givenfourintegers\(n\),\(a\),\(b\),and\(c\),returnthe\(n\)thuglynumber.Solution考虑如何二分答案,假设一个函数\(f(num,a,b,c)\)会得到\([1,num]\)中uglynumb......
  • LeetCode 875. Koko Eating Bananas 二分答案
    Kokolovestoeatbananas.Thereare\(n\)pilesofbananas,the\(i\)thpilehas\(piles[i]\)bananas.Theguardshavegoneandwillcomebackinhhours.Kokocandecideherbananas-per-houreatingspeedofk.Eachhour,shechoosessomepileofb......
  • LeetCode 1011. Capacity To Ship Packages Within D Days 二分答案
    Aconveyorbelthaspackagesthatmustbeshippedfromoneporttoanotherwithindaysdays.Theithpackageontheconveyorbelthasaweightof\(weights[i]\).Eachday,weloadtheshipwithpackagesontheconveyorbelt(intheordergivenby\(wei......
  • 二分专题训练
    KC喝咖啡题目描述:给\(n\)个物品,每个物品有两个属性\(v_i\)和\(c_i\),选出其中\(m\)件,最大化\(\frac{\sumv_i}{\sumc_i}\)。数据范围:\(1≤m≤n≤200\),\(1≤c_i,v_i≤1×10^4\)。01分数规划的板子题,不过很久没写过了还是记录一下。对于一个数值\(\lambda\),验证其是否符合条......
  • 二分图相关算法模板
    二分图判定概念解释二分图:设$G=(V,E)$是一个无向图,如果顶点$V$可分割为两个互不相交的子集$(A,B)$,并且图中的每条边$(i,j)$所关联的两个顶点$i$和$j$分别属于这两个不同的顶点集$(i\inA,j\inB)$,则称图$G$为一个二分图相关性质定理一个图是二分图$\Leftrightarrow$图中不......
  • LeetCode 852. Peak Index in a Mountain Array 二分
    Anarrayarramountainifthefollowingpropertieshold:arr.length>=3Thereexistssomeiwith0<i<arr.length-1suchthat:arr[0]<arr[1]<...<arr[i-1]<arr[i]arr[i]>arr[i+1]>...>arr[arr.length-......
  • 网络流与二分图
    补不完。太多了。CF1783FDoubleSortII先对排列建\(a_i\toi\)。交换\(i\)与\(a_i\)会使\(a_i\)原来所在环大小减1,证明画图理解。最后需要变为\(n\)个自环。把每个环集合抠出来,相当于每个环集合\(S\)中至少需要操作\(|S|-1\)个数。两个排列同理。我们发现操......
  • 二分查找
    二分查找本文的内容总结于该视频用二分查找来解决这四个问题时,边界条件很容易出错让我们从另一个角度来看这个问题:有红蓝两个指针,如何让这两个指针移动到各自的边界伪代码:对于上面的四种情况,我们只要控制IsBlue和要返回红色指针还是蓝色指针就可以做到以下是我用python......
  • LOJ #6160. 「美团 CodeM 初赛 Round A」二分图染色 思考--zhengjun
    link思维+容斥计数。首先的转化比较妙,二分图转化为\(n\timesn\)的网格图染色。与网络流的转化方向相反,值得注意。然后发现两种颜色(红、蓝)如果独立染色,同一个格子可能会重复染色。考虑容斥,式子很好列,直接容斥即可。\[ans=\sum\limits_{i=0}^n(-1)^n\times\binom{n}{i......