图论笔记5:二分图
二分图定义
二分图 : 不存在奇数环,染色法不存在矛盾
二分图染色
一、二分图染色原理
就不多说,就是利用dfs进行染色(bfs也行)也就不多说了,用一条边连结的相邻的两个点要不同颜色,若两个相邻的点同色,就说明了这个图不是个二分图。
二、二分图染色板子
基于链式前向星
const int M = 1e5 + 10;
const int N = 1e5 + 10;
struct edge
{
int next, to;
} e[M];
int head[N], color[N];
int n, m, cnt = 0;
void add(int u, int v)
{
e[++cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
bool dfs(int x, int c)
{
color[x] = c;
for (int i = head[x]; i; i = e[i].next)
{
int v = e[i].to;
if (!color[v])
{
if (!dfs(v, 3 - c))
return 0;
}
else if (color[v] == c)
return 0;
}
return 1;
}
bool check()
{
memset(color, 0, sizeof color);
bool flag = 1;
for (int i = 1; i <= n; i++)
if (!color[i] && !dfs(i, 1))
{
flag = 0;
break;
}
return flag;
}
void solve()
{
cin >> n >> m;
memset(head, 0, sizeof head);
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
add(u, v), add(v, u);
}
if (check())
cout << "Yes\n";
else
cout << "No\n";
}
三、模型
匈牙利算法
一、算法原理
简单来说就是,先按照一个顺序,先给第一个点匹配,然后再给第二个点匹配。。。。若当前需要匹配的点(记作点B)已经配匹配过了,那就用dfs查找匹配点B的点,是否能匹配下一个点,若可以,则将两点匹配,若不行,就将查找下一个与之相连的点是否符合条件,直到匹配,或者再没有能与之匹配的点。(可能有点乱,看看模板就可以理解)
二、算法模板
基于链式前向星
const int N = 510;
const int M = 1e5 + 10;
int n, m, k, cnt = 0;
int head[N], match[N];
bool bj[N];
bool maps[N][N];
struct edge
{
int next, to;
} e[M * 2];
void add(int u, int v)
{
e[++cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
int find(int x)
{
for (int i = head[x]; i; i = e[i].next)
{
int v = e[i].to;
if (!bj[v])
{
bj[v] = 1;
if (!match[v] || find(match[v]))
{
match[v] = x;
return 1;
}
}
}
return 0;
}
void solve()
{
cin >> n >> m >> k;
memset(match, 0, sizeof match);
memset(head, 0 ,sizeof head);
while (k--)
{
int u, v;
cin >> u >> v;
if (u == v || maps[u][v])
continue;
add(u, v);
maps[u][v] = 1;
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
memset(bj, 0, sizeof bj);
if (find(i))
ans++;
}
cout << ans << '\n';
}
基于邻接矩阵
int find(int x)
{
for (int i = 1; i <= m; i++)
{
if (!bj[i] && maps[x][i])
{
bj[i] = 1;
if (!match[i] || find(match[i]))
{
match[i] = x;
return 1;
}
}
}
return 0;
}
int solve()
{
int ans = 0;
for(int i = 1; i <= n; i++)
{
memset (bj, 0, sizeof bj);
if(find(i))
ans++;
}
return ans;
}
三、例题
1.过山车(fjutoj 1541)
http://120.78.128.11/Problem.jsp?pid=1541
纯板子题,就是将男女匹配,找最大匹配数就好了(套板子完事)
重要结论
最大匹配数 = 最小覆盖点 = 总点数 - 最大独立集 = 总点数 - 最小路径点覆盖
1.最小点覆盖
概念
最小顶点覆盖就是选择最少的点来覆盖所有的边。
在二分图中最小点覆盖等于最大匹配数。(一个定理)
证明
略。。。。。。。。
(作为一个没学过离散数学的ACMer来说,只能像放着等学了离散补上QAQ)
例题
1.机器任务(acwing 376)
https://www.acwing.com/problem/content/description/378/
这题可以将一个任务抽象成一条边,然后两种模式就是这条边的两个端点,如果想要执行此任务,则需要选出这两个端点的其中一个端点就好了,那不是变成了选出最少点,使所有边被覆盖,就是最小点覆盖,另外一开始两个机器都使模式0,那就是说,只要需要0模式的任务都可以在不重启的情况下执行,不要加边就好了。
2.Latin Square
题解见:
https://blog.csdn.net/qq_43085783/article/details/120836399?spm=1001.2014.3001.5501
2.最大独立集 和 最大团
概念
就是选出最多的点,使得这些点集中,任意两点都没有边。
最大团,则相反,选出最多点,使得任意两点间都有边。
证明
再一个二分图中,要选出最大独立集,则需要找出最少的点,用这些点,来破坏所有的边,那不就是找最小的点覆盖,由上方的结论得,就是找最大匹配。