二分图匹配

1. 过山车

题目链接:hdu - 2063

题意:求二分图的最大匹配

思路:二分图最大匹配模板题,利用匈牙利算法解决,其核心是通过不停地找增广路来增加匹配中的匹配边和匹配点,找不到增广路时,达到最大匹配

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

const int N = 1010;

struct node {
    int to, nex;
};

node edge[N * N];
int k, n, m, used[N], nex[N];
int cnt, head[N];

void add_edge(int u, int v)
{
    edge[++cnt].to = v;
    edge[cnt].nex = head[u];
    head[u] = cnt;
}

bool find(int u)
{
    for (int i = head[u]; 0 != i; i = edge[i].nex) {
        int v = edge[i].to;
        if (!used[v]) {
            used[v] = 1;
            if (nex[v] == 0 || find(nex[v])) {
                nex[v] = u;
                return true;
            }
        }
    }
    return false;
}

int match()
{
    int res = 0;
    for (int i = 1; i <= n; i++) {
        memset(used, 0, sizeof(used));
        if (find(i)) res++;
    }
    return res;
}

int main()
{
    while (scanf("%d", &k) && k) {
        cnt = 0;
        memset(head, 0, sizeof(head));
        memset(nex, 0, sizeof(nex));
        scanf("%d%d", &n, &m);
        while (k--) {
            int u, v;
            scanf("%d%d", &u, &v);
            add_edge(u, n + v);
        }
        printf("%d
", match());
    }
    return 0;
}
View Code

2. Antenna Placement

题目链接:poj - 3020

题意:在平面上有若干点,可以用一个圆圈覆盖相邻的两个点,问最少需要多少圆圈能将所有点覆盖

思路:二分图求最小路径覆盖,关键在于如何建图,可以采用黑白相间染色的办法,被染成黑色的点放在一个集合,被染成白色的点放在另一个集合,将相邻的黑色点和白色点之间连一条边,求出最大匹配数,二分图最小路径覆盖数=点的总数-二分图最大匹配数

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>

using namespace std;

const int N = 50;
const int M = 410;

struct node {
    int nex, to;
};

int T, h, w, n, m, b[N][N];
int used[M], nex[M], head[M], cnt;
node edge[M * M];
char s[N][N];

void add_edge(int u, int v)
{
    edge[++cnt].to = v;
    edge[cnt].nex = head[u];
    head[u] = cnt;
}

bool find(int u)
{
    for (int i = head[u]; 0 != i; i = edge[i].nex) {
        int v = edge[i].to;
        if (used[v]) continue;
        used[v] = 1;
        if (0 == nex[v] || find(nex[v])) {
            nex[v] = u;
            return true;
        }
    }
    return false;
}

int match()
{
    int res = 0;
    for (int i = 1; i <= n; i++) {
        memset(used, 0, sizeof(used));
        if (find(i)) res++;
    }
    return res;
}

void init()
{
    cnt = 0;
    memset(nex, 0, sizeof(nex));
    memset(head, 0, sizeof(head));
    memset(b, 0, sizeof(b));
}

int main()
{
    scanf("%d", &T);
    while (T--) {
        init();
        scanf("%d%d", &h, &w);
        for (int i = 1; i <= h; i++) scanf("%s", s[i] + 1);
        n = m = 0;
        for (int i = 1; i <= h; i++) {
            for (int k = 1; k <= w; k++) {
                if ('*' != s[i][k]) continue;
                if (0 == (i + k) % 2) b[i][k] = ++n;
                else b[i][k] = ++m;
            }
        }
        for (int i = 1; i <= h; i++) {
            for (int k = 1; k <= w; k++) {
                if ('*' != s[i][k] || 0 != (i + k) % 2) continue;
                if (b[i - 1][k]) add_edge(b[i][k], n + b[i - 1][k]);
                if (b[i + 1][k]) add_edge(b[i][k], n + b[i + 1][k]);
                if (b[i][k + 1]) add_edge(b[i][k], n + b[i][k + 1]);
                if (b[i][k - 1]) add_edge(b[i][k], n + b[i][k - 1]);
            }
        }
        int res = match();
        printf("%d
", n + m - res);
    }
    return 0;
}
View Code

3. Treasure Exploration

题目链接:poj - 2594

题意:给出一个由n个点m条边组成的有向无环图,求最少需要多少路径,使得这些路径可以覆盖所有点,每个点可以被多条路径覆盖

思路:二分图求最小路径覆盖,因为有的点可以被多条路径覆盖,可能存在相交路径,所以先要用floyd求出传递闭包,将求最小可相交路径转换成最小不可相交路径,然后再用二分图求最小路径覆盖,最小路径覆盖数=点的总数-二分图最大匹配数

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

const int N = 510;

int n, m, f[N][N];
int used[N], nex[N];

void floyd()
{
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                f[i][j] |= (f[i][k] & f[k][j]);
}

bool find(int u)
{
    for (int i = 1; i <= n; i++) {
        if (!used[i] && f[u][i]) {
            used[i] = 1;
            if (0 == nex[i] || find(nex[i])) {
                nex[i] = u;
                return true;
            }
        }
    }
    return false;
}

int match()
{
    int res = 0;
    for (int i = 1; i <= n; i++) {
        memset(used, 0, sizeof(used));
        if (find(i)) res++;
    }
    return res;
}

int main()
{
    while (scanf("%d%d", &n, &m) && 0 != (n + m)) {
        memset(nex, 0, sizeof(nex));
        memset(f, 0, sizeof(f));
        while (m--) {
            int u, v;
            scanf("%d%d", &u, &v);
            f[u][v] = 1;
        }
        floyd();
        int res = match();
        printf("%d
", n - res);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/zzzzzzy/p/12668023.html