题目类型 最小顶点覆盖
题目意思
给出最多10000个点的坐标 每次可以消除同一行的点或同一列的点 问至少要多少次才能把所有点都消除掉
解题方法
首先建图 每一个行号为一个点 每一个列号为一个点 那么对于一个要消除的点就对应一条这个点的行号与这个点的列号相连的边
那么消除所有点的目的就转化成对于每条边都要从这条边的两个端点找一个点与这条边关联, 即求最少的点让每条边都至少和其中的一个点关联
这就是最小顶点覆盖问题 可以证明 最小顶点覆盖数 = 最大匹配数M, 求最大匹配可以使用匈牙利算法
证明如下(黑书p332):
(1) M个是足够的。只需要让它们覆盖最大匹配的M条边,则其它边一定被覆盖 (如果有边e不被覆盖, 把e加入后得到一个更大的匹配)
(2) M个是必需的。仅考虑形成最大匹配边的这M条边, 由于它们两两无公共点, 因此至少需要M个点才能把它们覆盖
参考代码 - 有疑问的地方在下方留言 看到会尽快回复的
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 500 + 10;
vector<int>E[maxn];
bool vis[maxn];
int y[maxn];
bool dfs(int u) {
for( int i=0; i<E[u].size(); i++ ) {
int v = E[u][i];
if(vis[v]) continue;
vis[v] = true;
if(y[v] == 0 || dfs(y[v])) {
//printf("u = %d v = %d y[v] = %d\n", u, v, y[v]);
y[v] = u;
return true;
}
}
return false;
}
int main() {
freopen("in", "r", stdin);
int n, m;
while(scanf("%d%d", &n, &m) != EOF) {
for( int i=1; i<=n; i++ ) E[i].clear();
for( int i=0; i<m; i++ ) {
int u, v;
scanf("%d%d", &u, &v);
E[u].push_back(v);
}
int ans = 0;
memset(y, 0, sizeof(y));
for( int i=1; i<=n; i++ ) {
memset(vis, 0, sizeof(vis));
if(dfs(i)) ans++;
}
printf("%d\n", ans);
}
return 0;
}