AcWing 372. 棋盘覆盖(二分图/匈牙利算法)

题目

给定一个 N 行 N 列的棋盘,已知某些格子禁止放置。
求最多能往棋盘上放多少块的长度为 2、宽度为 1 的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),并且任意两张骨牌都不重叠。

输入输出

输入:第一行包含两个整数 N 和 t,其中 t 为禁止放置的格子的数量。
接下来 t 行每行包含两个整数 x 和 y,表示位于第 x 行第 y 列的格子禁止放置,行列数从 1 开始
输出:输出一个整数,表示结果。
1≤N≤100,
0≤t≤100

思路

将每个格子的坐标(i,j)相加得i+j,若i+j为奇数,涂黑色;若为偶数,涂白色。
可以看出棋盘中的格子和四周的颜色都不相同,则可以连一条边,表示放长度为1x2的骨牌。将格子为奇数的分为一个集合,格子为偶数的分为另一个集合,集合内部没有边,集合之间连边,说明是一个二分图。题意则是求二分图的最大匹配。

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

using namespace std;
typedef pair<int, int> PII;

const int N = 110;
bool g[N][N],st[N][N];
PII match[N][N];
int n,t;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

bool find(int x,int y){
    for (int i = 0; i < 4; i ++ ){
        int u = x + dx[i], v = y + dy[i];
        if(u <= 0 || u > n || v <= 0 || v > n)  continue;
        if(g[u][v]) continue;   //格子不能放
        if(!st[u][v]){
            st[u][v] = true;
            PII k = match[u][v];
            if((k.first == 0 && k.second == 0)|| find(k.first,k.second)){
                match[u][v] = {x,y};
                return true;
            }
        }
    }
    return false;
}

int main()
{
    cin >> n >> t;
    while (t --){
        int x, y;
        cin >> x >> y;
        g[x][y] = true; //true的格子不能放
    }
    int res = 0;
    for(int i = 1; i <= n; i ++){
        for(int j = 1; j <= n; j ++){
            if((i + j) % 2 && !g[i][j]){    //枚举一个集合
                memset(st, 0, sizeof st);
                if(find(i, j))  res ++;
            }
        }
    }
    cout << res;
    return 0;
}
posted @ 2021-08-05 13:13  inss!w!  阅读(33)  评论(0编辑  收藏  举报