搜索与图论3.1
作者:互联网
一、简述
本文章主要介绍有关二分图的两个基础算法。
二、二分图
二分图,它的端点可以被划分为两个集合,每个集合内部不含有边,边的两个端点分别位于两个集合。
关于二分图有一个性质,即一个图是二分图当且仅当该图不含有奇数环(一个环的边数为奇数,则这个环称为奇数环)。
三、染色法
模板题AcWing860.染色法判定二分图
题目描述
给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环。
请你判断这个图是否是二分图。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含两个整数 u 和 v,表示点 u 和点 v 之间存在一条边。
输出格式
如果给定图是二分图,则输出Yes
,否则输出No
。
数据范围
1≤n,m≤105
输入样例:
4 4
1 3
1 4
2 3
2 4
输出样例:
Yes
解题思路
既然前面提到二分图的端点可以划分为两个集合,则二分图可以被二染色。我们遍历所有端点 i ,如果 i 未被染色,则我们将 i 染色为 1 并遍历其所有邻接点并进行染色为 2 ,我们使用 1 和 2 表示两种颜色。
bool dfs(int u, int c)//u表示当前端点,c表示颜色
{
color[u] = c;
for(int i = h[u]; i != -1; i = ne[i])//遍历u的所有邻接点
{
int j = e[i];
if(!color[j])//如果未被染色
{
if(!dfs(j, 3 - c)) return false;//染为另一种颜色,失败则返回false
}
else if(color[j] == c) return false;//邻接点和u同色
}
return true;
}
C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010, M = 2 * N;
int n, m;
int h[N], e[M], ne[M], idx;
int color[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
bool dfs(int u, int c)
{
color[u] = c;
for(int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if(!color[j])
{
if(!dfs(j, 3 - c)) return false;
}
else if(color[j] == c) return false;
}
return true;
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for(int i = 0; i < m; i ++)
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
bool flag = true;
for(int i = 1; i <= n; i ++)
if(!color[i])
{
if(!dfs(i, 1))
{
flag = false;
break;
}
}
if(flag) puts("Yes");
else puts("No");
return 0;
}
四、匈牙利算法
匈牙利算法主要用来解决二分图的最大匹配问题和最小覆盖问题,此处我们先不讨论最小覆盖问题(笔者还未了解最小覆盖问题)并且也不展示匈牙利算法的证明,感兴趣的读者可以自行查找相关资料。
最大匹配问题
给定一个二分图G,在G的一个子图M中, M的边集{E}中的任意两条边都不交汇于同一个结点,则称M是一个匹配。选择边数最大的子图称为最大匹配问题。如果一个匹配中,图的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。
这么看有些抽象,此处引入来自y总给出的一个简单易懂的例子。
我们将二分图看作男生和女生两个集合,之间的边表示二者之间存在暧昧关系。那么最大匹配问题就变成了,我们是月老,我们可以成全最多多少对情侣。
我们从男生一方开始看(从女生一方也可以)。因为 b1 和 g2 存在暧昧关系,则我们先将 b1 和 g2 匹配(匹配时从上往下看)。红线表示匹配。
按照该规则,b2 可以和 g1 配对。
此时该到 b3 进行匹配了,我们发现 b3 仅和 g2 存在暧昧关系,而 g2 已经和 b1 匹配了。但是 b3 很顽强,不会就此罢休,此时我们就会看与 g2 匹配的 b1 是否和其他女生存在暧昧关系。b1 和 g4 存在暧昧关系,则 b1 就会去找 g4,成全 b3 和 g2。
最后将 b4 和 g3 匹配即可。
以上就是匈牙利算法的实现过程。
模板题AcWing 861.二分图的最大匹配问题
题目描述
给定一个二分图,其中左半部包含 n1 个点(编号 1∼n1),右半部包含 n2 个点(编号 1∼n2),二分图共包含 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
解题思路
思路就是上面使用匈牙利算法所实现的匹配过程。
C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 510, M = 100010;
int n1, n2, m;
int h[N], e[M], ne[M], idx;
int match[N];//女生的匹配对象
bool st[N];//女生是否匹配过
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
bool find(int x)
{
for(int i = h[x]; i != -1; i = ne[i])//遍历x的所有邻接点即男生的所有暧昧对象
{
int j = e[i];
if(!st[j])//未匹配
{
st[j] = true;//先匹配
if(match[j] == 0 || find(match[j]))//该女生没有匹配对象或者该女生的匹配对象可以找到其他的女生匹配
{
match[j] = x;
return true;
}
}
}
return false;
}
int main()
{
scanf("%d%d%d", &n1, &n2, &m);
memset(h, -1, sizeof h);
while(m --)
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
}
int res = 0;
for(int i = 1; i <= n1; i ++)
{
memset(st, false, sizeof st);
/*
st数组的赋值:假设左一和右一已经是一对,左二也看上了右一。
如果每次不更新st为false,左二发现match[1]=1,然后左一再去find(1),左一发现
match[1]=1,虽然就是他自己,但是他还是又去find(match[1]),所以就会一直递归下去。
如果每次把st更新为 false,左二每次考虑一个妹子再更新为true,那么左一在find(1)的时
候就会知道,这个妹子已经被左二考虑过了,下次可以不用再考虑这个妹子了。
st数组就保证了,只有当左一还有备胎的时候,才会把右一让给别人;同时也防止无限递归。
*/
if(find(i)) res ++;
}
printf("%d\n", res);
return 0;
}
标签:二分,图论,匹配,idx,int,color,搜索,3.1,return 来源: https://www.cnblogs.com/Cocoicobird/p/16671254.html