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
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];
vectore[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;
}
题后总结:
二分图匹配的模型有两个要素
- 节点能分成两个集合,每个集合内部有0条边。
- 每个节点只能与 1 条边相连。
我们简单把它称为 ”0要素“ 和 ”1要素“ 。在把实际问题抽象成二分图匹配时,我们就要寻找题目中具有这种 ”0“ 和 ”1“ 性质的对象从而发现构建模型的突破口。