匹配问题总结

2016.01.13 14:18 Wed| 14 visits oi_2016| 2016_刷题日常| Text

这些算法告诉我们,挑一个好代码去扒的重要性。

二分图中的某些定理

König定理:在二分图中,最大匹配 = 最小点覆盖。

带花树算法(一般图最大匹配)

在找交错路径的时候,要是发现了某两个偶点竟然是连在一起的,那么恭喜,你找到了一个奇环,也就是一朵花!如果交错路径经过这朵花,那么可以选择顺时针走还是逆时针走,也就是说花整个可以被缩成一个偶点。

KM 算法(二分图最大权完美匹配)

给两边的点都附上一个权值,规定每一条边的权值都必须小于边两边的点的权值和,而你只能走边权等于两边权值和的边进行匹配。最后要是有一种给点赋权的方案使得出现了完美匹配,你就获得了胜利,答案就是所有点的权值之和。

你要怎么附权呢?先让左面的点的权值等于从它连出去的路径中权值的最大值,右面的点权值为 0。尝试一次匹配,看右面有哪些点没被匹配上,顺便记录如果它的权值多了多少,它就能被匹配了,然后挑最小的更新一圈权值。什么时候全匹配上,什么时候算完。

HDU2063 过山车

话说我为什么要做二分图最大匹配的裸题啊摔!

 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
58
59
#include <bits/stdc++.h>
using namespace std;

#define N 1005
#define M 2005

int k, n, m, head[N], cnt, cp[N], v[N];

struct graph
{
    int next, to;
    graph() {}
    graph(int _next, int _to)
    : next(_next), to(_to) {}
} edge[M];

inline void add(int x, int y)
{
    edge[++cnt] = graph(head[x], y);
    head[x] = cnt;
}

bool match(int x)
{
    for (int i = head[x]; i; i = edge[i].next)
        if (!v[edge[i].to])
        {
            v[edge[i].to] = true;
            if (!cp[edge[i].to] || match(cp[edge[i].to]))
                { cp[x] = edge[i].to, cp[edge[i].to] = x; return true; }
        }
    return false;
}

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int main()
{
    while (cin >> k, k)
    {
        cin >> n >> m;
        memset(head, 0, sizeof head); cnt = 0;
        for (int x, y, i = 1; i <= k; ++i)
            x = read(), y = read() + n, add(x, y), add(y, x);
        int ans = 0;
        memset(cp, 0, sizeof cp);
        for (int i = 1; i <= n; ++i)
            if (!cp[i])
                memset(v, 0, sizeof v), ans += match(i);
        cout << ans << endl;
    }
    return 0;
}

URAL1099 Work Scheduling

带花树裸题。

 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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <bits/stdc++.h>
using namespace std;

#define N 300

int n, m, head[N], cp[N], pre[N], col[N], bel[N];
queue<int> q;

struct graph
{
    int next, to;
    graph() {}
    graph(int _next, int _to)
    : next(_next), to(_to) {}
} edge[900000];

inline void add(int x, int y)
{
    static int cnt = 1;
    edge[++cnt] = graph(head[x], y);
    head[x] = cnt;
}

int getlca(int x, int y)
{
    static int vis[N], tot;
    ++tot;
    while (true)
    {
        if (x)
        {
            if (vis[x] == tot) return x;
            vis[x] = tot, x = bel[pre[cp[x]]];
        }
        swap(x, y);
    }
}

void update(int x, int y, int lca)
{
    while (bel[x] != lca)
    {
        pre[x] = y, y = cp[x];
        if (col[y]) col[y] = 0, q.push(y);
        if (bel[x] == x) bel[x] = lca;
        if (bel[y] == y) bel[y] = lca;
        x = pre[y];
    }
}

bool match(int s)
{
    for (int i = 1; i <= n; ++i)
        bel[i] = i, col[i] = -1;
    while (!q.empty()) q.pop();
    col[s] = 0, q.push(s);
    while (!q.empty())
    {
        int x = q.front(); q.pop();
        for (int y, i = head[x]; i; i = edge[i].next)
            if (col[y = edge[i].to] == -1)
            {
                pre[y] = x, col[y] = 1;
                if (!cp[y])
                {
                    while (y)
                        cp[y] = pre[y], swap(cp[pre[y]], y);
                    return true;
                }
                col[cp[y]] = 0, q.push(cp[y]);
            }
            else if (!col[y])
            {
                int lca = getlca(x, y);
                update(x, y, lca), update(y, x, lca);
                for (int i = 1; i <= n; ++i)
                    bel[i] = bel[bel[i]];
            }
    }
    return false;
}

int main()
{
    cin >> n;
    int x, y;
    while (scanf("%d%d", &x, &y) != EOF)
        add(x, y), add(y, x);
    int ans = 0;
    for (int i = 1; i <= n; ++i)
        if (!cp[i]) ans += match(i);
    cout << ans * 2 << endl;
    for (int i = 1; i <= n; ++i)
        if (cp[i] > i)
            printf("%d %d\n", i, cp[i]);
    return 0;
}

URAL1076 Trash

答案为边权之和减去最大权完美匹配。

 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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <bits/stdc++.h>
using namespace std;

#define N 155
#define inf 0x3f3f3f3f

int n, m, d[N][N], cp[N], cp2[N];
int dl[N], dr[N], vl[N], vr[N], pre[N], slack[N];
queue<int> q;

void change(int x)
{
    while (x)
        cp[x] = pre[x], swap(cp2[pre[x]], x);
}

void match(int s)
{
    while (!q.empty()) q.pop();
    for (int i = 1; i <= n; ++i)
        slack[i] = inf, vl[i] = vr[i] = false;
    vl[s] = true, q.push(s);
    while (true)
    {
        while (!q.empty())
        {
            int x = q.front(); q.pop();
            for (int i = 1; i <= n; ++i)
            {
                if (vr[i]) continue;
                if (dl[x] + dr[i] == d[x][i])
                {
                    pre[i] = x, vr[i] = true;
                    if (!cp[i]) { change(i); return; }
                    else vl[cp[i]] = true, q.push(cp[i]);
                }
                else if (slack[i] > dl[x] + dr[i] - d[x][i])
                    slack[i] = dl[x] + dr[i] - d[x][i], pre[i] = x;
            }
        }
        int temp = inf;
        for (int i = 1; i <= n; ++i)
            if (!vr[i])
                temp = min(temp, slack[i]);
        for (int i = 1; i <= n; ++i)
        {
            if (vl[i]) dl[i] -= temp;
            if (vr[i]) dr[i] += temp;
            else slack[i] -= temp;
        }
        for (int i = 1; i <= n; ++i)
            if (!vr[i] && !slack[i])
            {
                if (!cp[i]) { change(i); return; }
                else vr[i] = vl[cp[i]] = true, q.push(cp[i]);
            }
    }
}

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int main()
{
    cin >> n;
    int ans = 0;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
            d[i][j] = read(),
            dl[i] = max(dl[i], d[i][j]), ans += d[i][j];
    for (int i = 1; i <= n; ++i)
        match(i);
    for (int i = 1; i <= n; ++i)
        ans -= dl[i] + dr[i];
    cout << ans << endl;
    return 0;
}

ZOJ3316 Game

判断一般图如果有完美匹配,那么后手胜利,否则先手总会胜利。

  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
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <bits/stdc++.h>
using namespace std;

#define N 505
#define M 250000

int n, m, len, x[N], y[N], head[N], cnt, cp[N], pre[N], col[N], bel[N];
queue<int> q;

struct graph
{
    int next, to;
    graph() {}
    graph(int _next, int _to)
    : next(_next), to(_to) {}
} edge[M];

inline void add(int x, int y)
{
    edge[++cnt] = graph(head[x], y);
    head[x] = cnt;
}

int getlca(int x, int y)
{
    static int vis[N], tot;
    ++tot;
    while (true)
    {
        if (x)
        {
            if (vis[x] == tot) return x;
            vis[x] = tot, x = bel[pre[cp[x]]];
        }
        swap(x, y);
    }
}

void update(int x, int y, int lca)
{
    while (bel[x] != lca)
    {
        pre[x] = y, y = cp[x];
        if (col[y]) col[y] = 0, q.push(y);
        if (x == bel[x]) bel[x] = lca;
        if (y == bel[y]) bel[y] = lca;
        x = pre[y];
    }
}

bool match(int s)
{
    for (int i = 1; i <= n; ++i)
        bel[i] = i, col[i] = -1;
    while (!q.empty()) q.pop();
    col[s] = 0, q.push(s);
    while (!q.empty())
    {
        int x = q.front(); q.pop();
        for (int y, i = head[x]; i; i = edge[i].next)
            if (col[y = edge[i].to] == -1)
            {
                pre[y] = x, col[y] = 1;
                if (!cp[y])
                {
                    while (y)
                        cp[y] = pre[y], swap(cp[pre[y]], y);
                    return true;
                }
                col[cp[y]] = 0, q.push(cp[y]);
            }
            else if (!col[y] && bel[x] != bel[y])
            {
                int lca = getlca(x, y);
                update(x, y, lca), update(y, x, lca);
                for (int i = 1; i <= n; ++i)
                    bel[i] = bel[bel[i]];
            }
    }
    return false;
}

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int main()
{
    while (cin >> n)
    {
        for (int i = 1; i <= n; ++i)
            x[i] = read(), y[i] = read();
        cin >> len;
        if (n & 1) { puts("NO"); continue; }
        memset(head, 0, sizeof head), cnt = 1;
        memset(cp, 0, sizeof cp);
        for (int i = 1; i <= n; ++i)
            for (int j = i + 1; j <= n; ++j)
                if (abs(x[i] - x[j]) + abs(y[i] - y[j]) <= len)
                    add(i, j), add(j, i);
        int ans = 0;
        for (int i = 1; i <= n; ++i)
            if (!cp[i]) ans += match(i);
        if (ans == n / 2) puts("YES");
        else puts("NO");
    }
    return 0;
}