【图论】二分图染色法与匈牙利算法

本文介绍了二分图的性质,详细讲解了如何使用二分图染色法解决CF741C Arpa’s overnight party and Mehrdad’s silent entering问题,并探讨了匈牙利算法在求解二分图最大匹配中的应用,包括洛谷P3386模板题的解法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

知识点


一 . 定义

二分图有个性质,如果一个图不是二分图,那么就一定有奇环;如果没有奇环,就一定是二分图


一 . 二分图染色法

例题:

CF741C Arpa’s overnight party and Mehrdad’s silent entering

题目描述

有2n个人围成一圈坐在桌子边上,每个人占据一个位子,对应这2n个人是n对情侣,要求情侣不能吃同一种食物,并且桌子上相邻的三个人的食物必须有两个人是不同的,只有两种食物(1或者是2),问一种可行分配方式。

输入格式

第一行为客人数量

接下来n行,第i+1行表示第i对情侣男女坐的位置

输出格式

无解输出-1

否则输出n行,第i+1行分别为第i组男女所食的种类

如果有多组解,输出任意一组解(spj)

输入输出样例

输入 #1

3
1 4
2 5
3 6

输出 #1

1 2
2 1
1 2
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
bool check=0;
const int maxn=1e6+5;
struct node{
	int to,next;
}edge[maxn<<1];
int head[maxn],num=0,colour=1;
int col[maxn],a[maxn],b[maxn];
inline int read()
{
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=(x<<3)+(x<<1)+(c^48);
		c=getchar();
	}
	return x*f;
}

inline void add(int u,int v)
{
	edge[++num].to=v;
	edge[num].next=head[u];
	head[u]=num;
}

inline void dfs(int u,int colour)  //二分图的染色
{
	col[u]=colour; 
	for(int i=head[u];i!=-1;i=edge[i].next)
	{
		int v=edge[i].to;
		if(col[v]) continue;
		dfs(v,3-colour); //与该点相连的点染成不同的颜色
	}
}

int main()
{
	n=read();
	memset(head,-1,sizeof(head));
	memset(col,0,sizeof(col));
	for(int i=1;i<=n;++i)
	{
		a[i]=read(); b[i]=read();
		add(a[i],b[i]); //先将情侣之间相互连线
		add(b[i],a[i]);
		add(i<<1,i<<1|1); //又因为要求2i和2i+1食物类型不同,相邻的两个点也要染成不同颜色
		add(i<<1|1,i<<1);
	}
	for(int i=1;i<=(n<<1);++i)
	{
		if(!col[i])	dfs(i,1); //一般二分图染色分别为1和2,0代表未染色
	}
	for(int i=1;i<=n;++i)
	{
		printf("%d %d\n",col[a[i]],col[b[i]]);
	}
	return 0;
}

相关练习

(待填坑)1. 洛谷 P1155 [NOIP2008 提高组] 双栈排序

这道题哪怕是看了题解也还是不明白是怎么想到交换字母顺序卡过数据的(๑ŐдŐ)

思路:根据输入的数据,需判断如何入栈才能保证按序输出从1~n 的值,即最小的应最先输出

如果在一个栈中,需要满足栈中元素 i < j 且a[i] > a[j] ,如果在两个栈中,栈中的三个元素需满足 k < i < j 且a[k] >a[i] > a[j]

那么,要如何将这些元素分配到两个栈中呢?

1\leqslant i < j < n。 若存在j < k \leqslant n 使得a[k] < a[i] < a[j] 则a[i],a[j]不能进入同一个栈
如果a[i],a[j]进入了同一个栈,且a[k] < a[i] < a[j] ,那么,出栈顺序为a[k]、a[i] 、a[j]
a[i]先入栈,a[j]入栈使需将a[i]出栈,但a[i]应在a[k]后入栈,相互矛盾。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
int n;
const int maxn=1e6+5,INF=0x3f3f3f3f;
struct node{
	int to,next;
}edge[maxn<<1];
int head[maxn],num=0;
int a[maxn],colour[maxn],Min[maxn];
bool vis[maxn];
char result[maxn];
inline int read()
{
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=(x<<3)+(x<<1)+(c^48);
		c=getchar();
	}
	return x*f;
}

inline void add(int u,int v)
{
	edge[++num].to=v;
	edge[num].next=head[u];
	head[u]=num;
}

inline void dfs(int num,int col)
{
	colour[num]=col;
	for(int i=head[num];i!=-1;i=edge[i].next)
	{
		int v=edge[i].to;
		if(!colour[v]) dfs(v,3-col);
		if(colour[v]==col) 
		{
			printf("0");
			exit(0);
		}
	}
}

int main()
{
	n=read();
	memset(head,-1,sizeof(head));
	memset(Min,INF,sizeof(Min));
	for(int i=1;i<=n;++i)
	{
		a[i]=read();
	}
	for(int i=n;i>=1;--i) //用于找到i,j,k三个数中的最小值(即分析中假设分成两个栈的情况)
	{
		Min[i]=min(a[i],Min[i+1]);
	}
	
	for(int i=1;i<=n-1;++i)
	{
		for(int j=i+1;j<=n;++j)
		{
			if((a[i]<a[j])&&(Min[j+1]<a[i])) //需要将两个数放在不同的集合中
			{
				add(i,j); add(j,i); //建立二分图
			}
		}
	}
	
	for(int i=1;i<=n;++i)
	{
		if(!colour[i]) dfs(i,1);
	}
	stack <int> s1;
	stack <int> s2;
	int check=1,sum=0;
	for(int i=1;i<=n;++i)
	{
		if(colour[i]==1) //二分图左边的元素
		{
		s1.push(a[i]);
		result[++sum]='a';
		}
		else 
		{
			s2.push(a[i]);
				result[++sum]='c';
		}
		while((!s1.empty()&&s1.top()==check)||(!s2.empty()&&s2.top()==check)) 
        //如果最两个栈其中一个顶端的元素是排成1~n在此次所找的数
		{
			if (!s1.empty()&&s1.top()==check) 
			{
			s1.pop();
			result[++sum]='b';
			}
			else
			{
			s2.pop();
			result[++sum]='d';
			}
			++check;
		}	
	}
	
	for(int i=sum-1;i>=1;--i) //从后往前找,缩小查找的范围
	{
		for(int j=i;j<sum;++j)
		{
			if(result[j]=='d'&&result[j+1]=='a')
			{
				swap(result[j],result[j+1]);
			}
			else break;
		}
	}
	
	for(int i=1;i<=sum;++i)
	{
		printf("%c ",result[i]);
	}
	return 0;
}


二 . 匈牙利算法

1 . 二分图最大匹配

求二分图最大匹配,就是从二分图中找出一条路径来,让路径的起点和终点都是还没有匹配过的点,并且路径经过的连线是一条没被匹配、一条已经匹配过,再下一条又没匹配这样交替地出现。找到这样的路径后,显然路径里没被匹配的连线比已经匹配了的连线多一条,于是修改匹配图,把路径里所有匹配过的连线去掉匹配关系,把没有匹配的连线变成匹配的,这样匹配数就比原来多1个。不断执行上述操作,直到找不到这样的路径为止。

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

题目描述

给定一个二分图,其左部点的个数为 n,右部点的个数为 m,边数为 e,求其最大匹配的边数。

左部点从 1 至 n 编号,右部点从 1 至 m 编号。

输入格式

输入的第一行是三个整数,分别代表 n,m 和 e。

接下来 e 行,每行两个整数 u, v,表示存在一条连接左部点 u 和右部点 v 的边。

输出格式

输出一行一个整数,代表二分图最大匹配的边数。

输入输出样例

输入 #1

1 1 1
1 1

输出 #1

1

输入 #2

4 2 7
3 1
1 2
3 2
1 1
4 2
4 1
1 1

输出 #2

2

说明/提示

数据规模与约定

对于全部的测试点,保证:

  • 1 \leqslant n, m \leqslant 500 。
  • 1 \leqslant e \leqslant 5 \times 10^4
  • 1 \leqslant u \leqslant n,1 \leqslant v \leqslant m 。

不保证给出的图没有重边

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值