题目地址:
https://www.acwing.com/problem/content/374/
给定一个 N N N行 N N N列的棋盘,已知某些格子禁止放置。求最多能往棋盘上放多少块的长度为 2 2 2、宽度为 1 1 1的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),并且任意两张骨牌都不重叠。
输入格式:
第一行包含两个整数
N
N
N和
t
t
t,其中
t
t
t为禁止放置的格子的数量。接下来
t
t
t行每行包含两个整数
x
x
x和
y
y
y,表示位于第
x
x
x行第
y
y
y列的格子禁止放置,行列数从
1
1
1开始。
输出格式:
输出一个整数,表示结果。
数据范围:
1
≤
N
≤
100
1≤N≤100
1≤N≤100
0
≤
t
≤
100
0≤t≤100
0≤t≤100
可以看成是二分图最大匹配问题。将棋盘的点看成图的点,并分为两大类,横纵坐标之和为偶数的为一类,剩余为一类,并且在相邻格子之间连边,这样这个图是个二分图,其某个匹配对应着某种骨牌摆放的方式。要使得能摆放的骨牌数最多,即要求最大匹配数,可以用匈牙利算法。只需注意在匹配的时候略过禁止摆放的格子即可。代码如下:
#include <iostream>
#include <cstring>
using namespace std;
// 点数大概是边长的平方,边数大概是两倍点数
const int N = 1e4 + 10, M = 2 * N * N, d[] = {1, 0, -1, 0, 1};
int n, m;
int h[N], e[M], ne[M], idx;
bool forb[N];
int match[N];
bool st[N];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// 二维转一维的公式
int get_pos(int x, int y) {
return (x - 1) * n + y;
}
bool dfs(int u) {
// 略过禁止摆放的格子
if (forb[u]) return false;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (st[j]) continue;
if (forb[j]) continue;
st[j] = true;
if (!match[j] || dfs(match[j])) {
match[j] = u;
return true;
}
}
return false;
}
int main() {
cin >> n >> m;
while (m--) {
int x, y;
cin >> x >> y;
forb[get_pos(x, y)] = true;
}
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if ((i + j) % 2 == 0) {
for (int k = 0; k < 4; ++k) {
int ni = i + d[k], nj = j + d[k + 1];
if (1 <= ni && ni <= n && 1 <= nj && nj <= n)
add(get_pos(i, j), get_pos(ni, nj));
}
}
int res = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if ((i + j) % 2 == 0) {
memset(st, 0, sizeof st);
if (dfs(get_pos(i, j))) res++;
}
cout << res << endl;
return 0;
}
时间复杂度 O ( n 3 ) O(n^3) O(n3),空间 O ( n 2 ) O(n^2) O(n2), n n n为棋盘边长。