POJ2226(二分图建图/最小点覆盖)

题意:
给定m*n的棋盘,有若干只咕咕。希望去掉一部分咕咕使得剩下的咕咕在上下左右四个方向越过咕咕槽的情况下都看不到咕咕。
思路:
建立一个二分图的方法有很多,这里采用xy二分。
假设没有咕咕槽的情况下,咕咕的最大放置数其实有点像八皇后问题,当然这里我们用最大匹配的思路求解。
当一个位置上放置了咕咕,那么其坐标(x,y)便被移除了待选集合,由此可见,如果建立一个X和Y的二分图,那么最大咕咕数就是二分图的最大匹配。
有锅的时候,相比于没有锅的时候难度增加在了怎么建边上,我们仍然想通过编号的方式建边,那么对于每个横向、竖向区域便可以可以用他们最左边\最右边的区域的编号代替,将其建边求最大二分匹配即可。(说见了就是锅的右边行+1,锅的下边列+1)。

//https://blog.csdn.net/sotifish/article/details/48396087
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
const int maxn = 10005;
vector<int> G[maxn];
char s[maxn][maxn];
bool vis[maxn];
int match[maxn];
int n, m;

void add_edge(int u, int v) {
    G[u].push_back(v);
    G[v].push_back(u);
}

bool dfs(int u) {
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (!vis[v]) {
            vis[v] = true;
            if (match[v] < 0 || dfs(match[v])) {
                match[v] = u;
                return true;
            }
        }
    }
    return false;
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(match, -1, sizeof(match));
    for (int i = 0; i < n; i++) {
        scanf("%s", s[i]);
    }
    int cnt = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (s[i][j] == 'o') {
                int x = i, y = j;
                while (x > 0 && s[x - 1][j] != '+') x--;
                while (y > 0 && s[i][y - 1] != '+') y--;
                add_edge(x*m + j, i*m + y + m*n);
                if (s[i][j] == 'o')cnt++;
            }
        }
    }
    int res = 0, num;
    for (int i = 0; i < n*m; i++)
    {
        memset(vis, false, sizeof(vis));
        if (dfs(i)) res++;
    }
    if (cnt <= res || cnt == 0) num = 0;
    else num = cnt - res;
    printf("%d\n", num);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38995588/article/details/80621414