二分图最大匹配 372. 棋盘覆盖 - AcWing题库 给定一个 行 列的棋盘,已知某些格子禁止放置。
求最多能往棋盘上放多少块的长度为 、宽度为 的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),并且任意两张骨牌都不重叠。
输入格式
第一行包含两个整数 和 ,其中 为禁止放置的格子的数量。
接下来 行每行包含两个整数 和 ,表示位于第 行第 列的格子禁止放置,行列数从 开始。
输出格式
输出一个整数,表示结果。
数据范围
,
输入样例:
8 0
输出样例:
32
思路
给一个 的棋盘, 用两格的骨牌填满, 并且有些格子无法放置, 求能填多少个。
乍一看和状压DP的题目很像, 不过求的是可行解, 那可以用搜索, 但格子一共 也就是 , 爆搜复杂度得是 , 肯定超时。
每个骨牌只占两格, 且只有这一种骨牌, 若把每个格子看做一个点, 相邻的格子连一条边, 那么就是求这种匹配对最多有几个, 即最大匹配问题, 而且是两个点匹配, 那么就是二分图。
最大匹配问题可以用匈牙利算法来实现:
- 定义 数组表示每个点对应匹配的另一个点
- 对于点 , 枚举它能匹配到的所有点
- 若 未被匹配, 或者可以通过 操作使得原本匹配 的点换一个点匹配, 则将
而该题的点是以坐标形式表示, 那么 数组也需要开成二维且类型为 pair。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const int N = 1e2 + 10;
typedef pair<int,int> PII;
int g[N][N];
PII match[N][N];
int st[N][N];
int n,m;
int dx[] = {0,0,1,-1}, dy[] = {1,-1,0,0};
bool find(int x, int y)
{
for(int i = 0; i < 4; i++)
{
int a = x + dx[i], b = y + dy[i];
if(a < 1 || a > n || b < 1 || b > n) continue;
if(st[a][b] || g[a][b]) continue;
st[a][b] = true;
PII t = match[a][b];
if(t.first == 0 || find(t.first, t.second))
{
match[a][b] = {x,y};
return true;
}
}
return false;
}
int main()
{
cin >> n >> m;
while(m--)
{
int a, b;
cin >> a >> b;
g[a][b] = 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 << endl;
return 0;
}