欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

最大匹配(二分图和一般无向图)

程序员文章站 2024-01-04 15:59:10
...

二分图匈牙利算法

#include <bits/stdc++.h>

using namespace std;
const int SIZE = 1e5 + 10;
int head[SIZE], ver[SIZE * 2], Next[SIZE * 2];
int n, m, tot;
bool visit[SIZE];
int match[SIZE];
int ans;

void add(int x, int y) {
    ver[++tot] = y;
    Next[tot] = head[x];
    head[x] = tot;
}

bool dfs(int x) {
    for (int i = head[x]; i; i = Next[i]) {
        int y=ver[i];
        if (!visit[y]) {
            visit[y] = true;
            if (!match[y] || dfs(match[y])) {
                match[y] = x;
                return true;
            }
        }
    }
    return false;
}

int main() {
    cin >> n >> m;
    tot = 1;
    for (int i = 1; i <= m; i++) {
        int x, y;
        cin>>x>>y;
        add(x,y);
        add(y,x);
    }
    for (int i = 1; i <= n; i++) {
        memset(visit, false, sizeof(visit));
        if (dfs(i)) {
            ans++;
            if (match[i])
                cout << i << ' ' << match[i] << endl;
        }
    }
    cout << ans;
    return 0;
}
input:
10 12
1 6
1 7
2 8
3 6
3 7
3 8
3 9
4 8
4 9
5 8
5 9
5 10
output:
6 1
7 3
8 2
9 4
10 5
10

一般无向图最大匹配(别人的代码)

#include <bits/stdc++.h>

using namespace std;

#define swap(x, y) {int tt=x;x=y;y=tt;}
const int N = 505 * 2;
const int M = 124750 * 2;
int f[N];
struct qq {
    int x, y;
    int last;
} s[M];
int num, last[N];
int n, m;

void init(int x, int y) {
    num++;
    s[num].x = x;
    s[num].y = y;
    s[num].last = last[x];
    last[x] = num;
}

int match[N];
int id[N];//这个点是什么点
//-1:没访问过  0:S点  1:T点
int q[N];//要扩展的队列————也就是我们要尝试帮谁换配偶
int pre[N];//在这次过程中,x的新配偶是谁
int Tim, vis[N];//对于lca的标记以及时间轴
int find(int x) {
    if (f[x] == x) return f[x];
    f[x] = find(f[x]);
    return f[x];
}

int lca(int x, int y)//寻找lca
{
    Tim++;
    while (vis[x] != Tim) {
        if (x != 0) {
            x = find(x);//先找到花根
            if (vis[x] == Tim) return x;
            vis[x] = Tim;
            if (match[x] != 0) x = find(pre[match[x]]);
                //因为在之前我们知道,每一个S点的配偶(也就是T点)的pre 都是指向他的父亲的,于是就直接这么跳就可以了
                //还有要注意的是,一定要先去到花根,因为他们现在已经是一个点了,只有花根的pre才指向他们真正的父亲
            else x = 0;
        }
        swap(x, y);
    }
    return x;
}

int st, ed;

void change(int x, int y, int k)//环  出现的是x---y的连边  已知根是k
{
    while (find(x) != k) {
        pre[x] = y;
        int z = match[x];
        id[z] = 0;
        q[ed++] = z;
        if (ed >= N - 1) ed = 1;
        if (find(z) == z) f[z] = k;
        if (find(x) == x) f[x] = k;
        y = z;
        x = pre[y];
    }
}

void check(int X)//尽量帮助x寻找增广路
{
    for (int u = 1; u <= n; u++) {
        f[u] = u;
        id[u] = -1;
    }
    st = 1;
    ed = 2;
    q[st] = X;
    id[X] = 0;
    while (st != ed) {
        int x = q[st];
        for (int u = last[x]; u != -1; u = s[u].last) {
            int y = s[u].y;
            if (match[y] == 0 && y != X)
                //当然match[X]=0,但X(这次来寻找配偶的点)并不是一个可行的东西,所以不能算可行解
            {
                pre[y] = x;//先假设他与x相连
                int last, t, now = y;
                while (now != 0)//当然,这次来的X的match是为0,要是能更新到0就是结束
                {
                    t = pre[now];//now新的配偶
                    last = match[t];//理所当然啦
                    match[t] = now;
                    match[now] = t;
                    now = last;
                }
                return;
            }
            if (id[y] == -1)//找到一个没有访问过的点————进行扩展
            {
                id[y] = 1;
                pre[y] = x;//先假设他与x相连
                id[match[y]] = 0;
                q[ed++] = match[y];
                if (ed >= N - 1) ed = 1;
            } else if (id[y] == 0 && find(x) != find(y))//出现一个以前未处理过的奇环
            {
                int g = lca(x, y);
                change(x, y, g);
                change(y, x, g);
            }
        }
        st++;
        if (st >= N - 1) st = 1;
    }
}

int main() {
    memset(vis, 0, sizeof(vis));
    Tim = 0;
    memset(match, 0, sizeof(match));
    num = 0;
    memset(last, -1, sizeof(last));
    scanf("%d%d", &n, &m);
    for (int u = 1; u <= m; u++) {
        int x, y;
        scanf("%d%d", &x, &y);
        init(x, y);
        init(y, x);
    }
    for (int u = 1; u <= n; u++)
        if (match[u] == 0)
            check(u);
    int ans = 0;
    for (int u = 1; u <= n; u++)
        if (match[u] != 0) ans++;
    set<int> remain;
    for (int i = 1; i <= n; i++)
        remain.insert(i);
    while (!remain.empty()) {
        int i = *(remain.begin());
        if (match[i] != 0)
            printf("%d %d\n", i, match[i]);
        remain.erase(i);
        remain.erase(match[i]);
    }
    printf("%d\n", ans);
    return 0;
}
input:
10 12
1 6
1 7
2 8
3 6
3 7
3 8
3 9
4 8
4 9
5 8
5 9
5 10
output:
1 7
2 8
3 6
4 9
5 10
10
相关标签: 算法模板

上一篇:

下一篇: