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;
}