【图论】二分图的判定 学习笔记

二分图的判定

记无向图 \(G = (V, E)\),若存在点集 \(A,B\) 满足:

  1. \(A \cup B = V\)
  2. \(A \cap B = \varnothing\)
  3. \(\forall e = (u,v) \in E\), 满足 \(u,v\) 不同时在 \(A\)\(B\) 中。

则称图 \(G\) 为二分图,\(A,B\) 分别称作二分图的左部与右部。

二分图 - OI Wiki

二分图的判定定理

下面三个命题等价,可互相推出。

  1. \(G\) 是一个二分图,满足上述的可划分性。
  2. \(G\) 中不存在长度为奇数的环。
  3. \(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;
}
posted @ 2023-10-18 21:00  蒟蒻OIer-zaochen  阅读(33)  评论(0编辑  收藏  举报