【图论】二分图的判定 学习笔记
二分图的判定
记无向图 \(G = (V, E)\),若存在点集 \(A,B\) 满足:
- \(A \cup B = V\)
- \(A \cap B = \varnothing\)
- \(\forall e = (u,v) \in E\), 满足 \(u,v\) 不同时在 \(A\) 或 \(B\) 中。
则称图 \(G\) 为二分图,\(A,B\) 分别称作二分图的左部与右部。
二分图的判定定理
下面三个命题等价,可互相推出。
- 图 \(G\) 是一个二分图,满足上述的可划分性。
- 图 \(G\) 中不存在长度为奇数的环。
- 图 \(G\) 可被二染色。
其中,\(1 \implies 2\) 因为每一条边都是从一个集合走到另一个集合,只有走偶数次才可能回到同一个集合(from OI Wiki),\(3\) 中二染色表示可以把所有点染成两种颜色中的一种,对于 \(\forall e = (u,v) \in E\),满足 \(u,v\) 的颜色不同。
所以可以使用染色法判定二分图,具体代码实现见例题。
例题
[NOIP2010 提高组] 关押罪犯
答案具有二段性:即如果影响力为 \(x\) 时可以分配罪犯,则影响力大于 \(x\) 时也可以分配罪犯,于是可以二分答案。
二分答案的判定可以用二分图的判定来求解:把每个罪犯当作一个节点,每对怨气值当作一条无向边,若 \(c < z\) 则可以当作无这条边,因为就算这条边的权值更新答案也不会让答案变大。
对于剩下的边进行二分图的判定,求出的二分图两个部中的点分别分到两个监狱里就是合法的解。
参考代码:
#include <bits/stdc++.h>
using namespace std;
// #define int long long
const int N = 2e4 + 5, M = 2e5 + 5;
int n, head[N], color[N], nxt[M], to[M], edgecnt;
void g_clear() { memset(head, 0, (n + 5) * sizeof(head[0])), memset(color, 0, (n + 5) * sizeof(color[0])), edgecnt = 0; };
void g_add(int u, int v) { to[++edgecnt] = v, nxt[edgecnt] = head[u], head[u] = edgecnt; };
int m, a[M], b[M], c[M];
bool dfs(int u, int cl)
{
color[u] = cl;
for (int e = head[u]; e; e = nxt[e])
{
int v = to[e], c = 3 - cl;
if (color[v] && color[v] != c)
return false;
if (!color[v] && !dfs(v, 3 - cl))
return false;
}
return true;
}
bool check(int x)
{
g_clear();
for (int e = 1; e <= m; e++)
if (c[e] > x)
g_add(a[e], b[e]), g_add(b[e], a[e]);
bool res = true;
for (int vec = 1; vec <= n; vec++)
if (!color[vec])
res &= dfs(vec, 1);
return res;
}
signed main()
{
ios::sync_with_stdio(false);
#ifdef LUOTIANYI
clock_t t0 = clock();
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif
// Don't stop. Don't hide. Follow the light, and you'll find tomorrow.
cin >> n >> m;
for (int e = 1; e <= m; e++)
cin >> a[e] >> b[e] >> c[e];
int l = 0, r = *max_element(c + 1, c + 1 + m);
while (l < r)
{
int mid = (l + r) / 2;
if (check(mid))
r = mid;
else
l = mid + 1;
}
cout << l << endl;
#ifdef LUOTIANYI
cerr << "Time used:" << clock() - t0 << "ms" << endl;
#endif
return 0;
}