二分图,匈牙利算法

dfs写法

const int maxn = 100024;

struct nobe {
    int to;
    int lst;
}edge[maxn];
int head[128];
int qsz = 1;

inline void add(int u, int v) {
    edge[qsz].lst = head[u];
    edge[qsz].to  = v;
    head[u] = qsz++;    
}

int match[128];
bool check[128];

bool dfs(int u) {
    int i, v;
    for (i=head[u]; i; i=edge[i].lst) {
        v = edge[i].to;
        if (!check[v]) {
            check[v] = true;
            if (match[v]==-1 || dfs(match[v])) {
                match[v] = u;
                match[u] = v;
                return true;
            }
        }
    }
    return false;
}

// 返回匹配个数  匹配结果存储在matching中  
int GetAns(int n) {
    int ans = 0;
    memset(match, -1, sizeof(match));
    for (int i=1; i<=n; ++i) {  // i是从一个匹配集合开始for的,(左边集合 或者 右边集合.) 
        if (match[i] == -1) {
            memset(check, 0, sizeof(check));
            ans += dfs(i);
        }
    }
    return ans;
}

bfs写法

int match[128];   // 匹配的边   
int   vis[128];  // 注意是int类型 
int prevv[128]; //  

int bfs(int m) {
    queue<int> Q;
    int i, j, u, v, ans = 0;
    memset(match, -1, sizeof(match));
    memset(vis, -1, sizeof(vis));
    for (i=1; i<=m; ++i) {
        if (match[i] == -1) {
            while (!Q.empty()) Q.pop();
            Q.push(i);
            prevv[i] = -1;
            bool flag = false;
            while (!Q.empty() && !flag) {
                u = Q.front();
                for (j=head[u]; j && !flag; j=edge[j].lst) {
                    v = edge[j].to;
                    if (vis[v] != i) {
                        vis[v] = i;
                        Q.push(match[v]);
                        if (match[v] >= 0) {
                            prevv[match[v]] = u;
                        } else {
                            flag = true;
                            int d = u, e = v;
                            while (d != -1) {
                                int t = match[d];
                                match[d] = e;
                                match[e] = d;
                                d = prevv[d];
                                e = t;
                            }
                        }
                    }
                }
                Q.pop();
            }
            if (match[i] != -1) ans++;
        }
    }
    return ans;
} 

同时bfs优于dfs,但是dfs好理解.

附上学习的博客  : http://www.renfei.org/blog/bipartite-matching.html

然后附上一些性质

补充定义和定理:

最大匹配数:最大匹配的匹配边的数目

最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择

最大独立数:选取最多的点,使任意所选两点均不相连

最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。

定理1:最大匹配数 = 最小点覆盖数(这是 Konig 定理)

定理2:最大匹配数 = 最大独立数

定理3:最小路径覆盖数 = 顶点数 - 最大匹配数

百科说: 最大匹配数=最大流=最小割=最小点集覆盖

有向无环图的最大匹配数.

https://vjudge.net/problem/HDU-1151  

const int maxn = 100024;
struct nobe {
    int to;
    int lst;
}edge[maxn];
int head[128];
int qsz = 1;

inline void add(int u, int v) {
    edge[qsz].lst = head[u];
    edge[qsz].to  = v;
    head[u] = qsz++;    
}

int  match[128];
int  vis[128];

bool dfs(int u) {
    int i, v;
    for (i=head[u]; i; i=edge[i].lst) {
        v = edge[i].to;
        if (vis[v]) continue;
        vis[v] = true;
        if (match[v]==-1 || dfs(match[v])) {
            match[v] = u;
            return true;
        }
    }    
    return false;
}

int main()
{
    int t, n, m, i;
    scanf("%d", &t);
    while (t--) {
        memset(head, 0, sizeof(head));
        scanf("%d%d", &n, &m);
        int u, v;
        while (m--) {
            scanf("%d%d", &u, &v);
            add(u, v);
        }     
        int res = 0;
        memset(match, -1, sizeof(match));
        for (i=1; i<=n; ++i) {
            memset(vis, 0, sizeof(vis));
            res += dfs(i);
        }
        printf("%d
", n - res);
    }

    return 0;
}

无向图的最大匹配数.

https://vjudge.net/problem/HihoCoder-1122

int  match[10024];
bool   vis[10024];

bool dfs(int u) {
    int i, v;
    for (i=head[u]; i; i=edge[i].lst) {
        v = edge[i].to;
        if (!vis[v]) {
            vis[v] = true;
            if (match[v]==-1 || dfs(match[v])) {
                match[v] = u;
                match[u] = v;
                return true;
            }
        }
    }
    return false;
}

int main()
{
    int u, v, i, n, m;
    scanf("%d%d", &n, &m);
    while (m--) {
        scanf("%d%d", &u, &v);
        add(u, v);
        add(v, u); // 双向边. 听说有些情况是要答案除以2的
    }
    
    int res = 0;
    memset(match, -1, sizeof(int) * (n + 10));
    for (i=1; i<=n; i++) {
        memset(vis, 0, sizeof(int) * (n + 10));        
        if (match[i] == -1) res += dfs(i);
    }
    printf("%d
", res);
    
    return 0;
}
原文地址:https://www.cnblogs.com/cgjh/p/9516001.html