跳转至

介绍二分图

定义

二分图,又称二部图,英文名叫 Bipartite graph。

二分图是什么?节点由两个集合组成,且两个集合内部没有边的图。

换言之,存在一种方案,将节点划分成满足以上性质的两个集合。

性质

  • 如果两个集合中的点分别染成黑色和白色,可以发现二分图中的每一条边都一定是连接一个黑色点和一个白色点。

  • 二分图不存在长度为奇数的环
    1
    因为每一条边都是从一个集合走到另一个集合,只有走偶数次才可能回到同一个集合。
    

判定

如何判定一个图是不是二分图呢?

换言之,我们需要知道是否可以将图中的顶点分成两个满足条件的集合。

显然,直接枚举答案集合的话实在是太慢了,我们需要更高效的方法。

考虑上文提到的性质,我们可以使用 DFS 或者 BFS 来遍历这张图。如果发现了奇环,那么就不是二分图,否则是。

应用

染色法判定二分图

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#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() {
    cin >> n >> m;

    memset(h, -1, sizeof h);

    while (m--) {
        int a, b;
        cin >> 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;
            }

    for (int i = 1; i <= n; i++)
        printf("%d ", color[i]);

    if (flag)
        puts("Yes");
    else
        puts("No");

    return 0;
}

匈牙利算法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#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]) {
        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() {
    cin >> n1 >> n2 >> m;

    memset(h, -1, sizeof h);

    while (m--) {
        int a, b;
        cin >> a >> b;

        add(a, b);
    }

    int res = 0;
    for (int i = 1; i <= n1; i++) {
        memset(st, false, sizeof st);
        if (find(i))
            res++;
    }

    cout << res << endl;

    return 0;
}
回到页面顶部