给定一个N行N列的棋盘,已知某些格子禁止放置。
求最多能往棋盘上放多少块的长度为2、宽度为1的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),并且任意两张骨牌都不重叠。
输入格式
第一行包含两个整数N和t,其中t为禁止放置的格子的数量。
接下来t行每行包含两个整数x和y,表示位于第x行第y列的格子禁止放置,行列数从1开始。
输出格式
输出一个整数,表示结果。
数据范围
1≤N≤100
输出样例:
8 0
输出样例:
32
二分图匹配问题,把骨牌当作边,棋盘上的点当作图中的节点,棋盘中放置骨牌必须是两个相邻的点,而两个相邻的点的坐标和(x + y)的奇偶性不同,因此可以将所有的点通过坐标和的奇偶性分成两部分,看作是二分图的两个点集,再用匈牙利跑二分图就好了。建图的时候注意,因为我们跑最大匹配是从一个集合出发匹配另一个集合,而这个图我们需要跑所有的点,因此有两种解法。
1、遍历所有的点求最大匹配,结果除以2
2、只遍历二分图的左部或者右部(即奇偶性相同的一个点集)
我数组开小了,改bug改了好久QWQ
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 300;
int g[N][N];
int head[N * N], nex[N * N], to[N * N], cnt, match[N * N];
bool vis[N * N];
int dx[] = { 1, -1, 0, 0 };
int dy[] = { 0, 0, 1, -1 };
void init() {
memset(g, 0, sizeof g);
memset(head, -1, sizeof head);
memset(nex, -1, sizeof nex);
memset(vis, false, sizeof vis);
memset(match, -1, sizeof match);
cnt = 0;
}
void add(int a, int b) {
cnt++;
nex[cnt] = head[a];
head[a] = cnt;
to[cnt] = b;
}
bool dfs(int p) {
for (int i = head[p]; i != -1; i = nex[i]) {
int y = to[i];
if (!vis[y]) {
vis[y] = true;
if (match[y] == -1 || dfs(match[y])) {
match[y] = p;
return true;
}
}
}
return false;
}
int main()
{
int n, t;
ios::sync_with_stdio(0);
init();
cin >> n >> t;
while (t--) {
int a, b;
cin >> a >> b;
g[a][b] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (g[i][j] == 0) {
for (int k = 0; k < 4; k++) {
int xx = i + dx[k];
int yy = j + dy[k];
if (xx >= 1 && xx <= n && yy >= 1 && yy <= n && g[xx][yy] == 0) {
add((i - 1) * n + j, (xx - 1) * n + yy);
}
}
}
}
}
int res = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if ((i + j) % 2 == 1)continue;
memset(vis, false, sizeof vis);
if (dfs((i - 1) * n + j))res++;
}
}
cout << res << "\n";
return 0;
}