U64949 棋盘覆盖(二分图)| 二分图匹配总结

https://ac.nowcoder.com/acm/contest/1062/B

【题目】

给出一张n×n(n≤100)的国际象棋棋盘,其中被删除了一些点,问可以使用多少1*2的多米诺骨牌进行掩盖。

【题意】

题意简单,不做多说明,多米诺骨牌可以理解为长方形的方块。

【题解】

仔细一想,可以发现能用二分图来做。即可以把每个位置的点进行重新编号,相邻的两点具有不同的性质。

比如说在2×2的图内第一个点\((1,1)\)标记为1,它是奇数,那么与它相邻的\((1,2)(1,2)\)就要标记成偶数。又比如在3×3的图内的点\((2,2)\)为奇数,那么\((1,2),(2,1)(2,3),(3,2)\)的点就要标记为偶数。然后两两建边,奇数点->偶数点 or 偶数点->奇数点(当然如果是被删除的点,则不能建边)。最后对 偶数点 or 奇数点 跟 奇数点 or 偶数点 进行二分图匹配即可。

时间复杂度:\(O(N^2M^2)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 110;
const int dx[] = { 0,1,0,-1 };
const int dy[] = { 1,0,-1,0 };
int n, m, ans, f[N * N];
bool b[N][N], v[N * N];
vector<int>e[N * N];

bool dfs(int x) {
    for (unsigned int i = 0; i < e[x].size(); i++) {
        int y = e[x][i];
        if (v[y]) continue;
        v[y] = 1;
        if (f[y] == -1 || dfs(f[y])) {
            f[y] = x;return 1;
        }
    }
    return 0;
}

int main() {
    //freopen("in.txt", "r", stdin);
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n >> m;
    while (m--) {
        int x, y; cin >> x >> y;
        b[x - 1][y - 1] = 1;
    }
    for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			if (!b[i][j])
				for (int k = 0; k < 4; k++) {
					int x = i + dx[k], y = j + dy[k];
					if (x >= 0 && x < n && y >= 0 && y < n && !b[x][y]) {
						e[i*n+j].push_back(x * n + y);
						e[x*n+y].push_back(i * n + j);
					}
				}
    memset(f, -1, sizeof(f));
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++) {
            if ((i ^ j) & 1) continue;
            memset(v, 0, sizeof(v));
            ans += dfs(i * n + j);
        }
    cout << ans << endl;
}

题后总结:

二分图匹配的模型有两个要素

  1. 节点能分成两个集合,每个集合内部有0条边。
  2. 每个节点只能与 1 条边相连。

我们简单把它称为 ”0要素“ 和 ”1要素“ 。在把实际问题抽象成二分图匹配时,我们就要寻找题目中具有这种 ”0“ 和 ”1“ 性质的对象从而发现构建模型的突破口。

posted @ 2020-08-11 15:35  RioTian  阅读(369)  评论(0编辑  收藏  举报