题目链接:【http://acm.hdu.edu.cn/showproblem.php?pid=1498】
题意:给出一个大小为n*n(0<n<100)的矩阵,矩阵中放入m种颜色(标号为1-m),然后对于每种颜色有k次操作,每次操作可以选择一行或者一列,然后把这一行或者这一列的这种颜色全部去掉,求每种颜色k次操作以后,不能去掉的颜色是哪几种颜色,从小到大输出。
最小顶点覆盖要求用最少的点(X或Y中都行),让每条边都至少和其中一个点关联。Knoig定理:二分图的最小顶点覆盖数等于二分图的最大匹配数。
横坐标映射成X点集合,纵坐标映射成Y点,把每个矩阵中相同颜色点的坐标(x,y)建立一条边x->y,目的是选择最少的点把所有的边(即:矩阵中的点)覆盖。
#include<bits/stdc++.h> using namespace std; const int maxn = 150; int n, k; struct node { int u, v; }; vector<node>E[55]; vector<int>Edge[maxn], ans; void init() { for(int i = 1; i <= 52; i++) E[i].clear(); ans.clear(); } int fa[maxn], vis[maxn]; bool match(int u) { int l = Edge[u].size(); for(int i = 0; i < l; i++) { int v = Edge[u][i]; if(vis[v]) continue; vis[v] = 1; if(fa[v] == -1 || match(fa[v])) { fa[v] = u; return true; } } return false; } int hungry() { int ret = 0; memset(fa, -1, sizeof(fa)); for(int i = 1; i <= n; i++) { memset(vis, 0, sizeof(vis)); if(match(i)) ret++; } return ret; } int main () { while(scanf("%d%d", &n, &k)) { if(n == 0 && k == 0) break; init(); int t, ma = 0; for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { scanf("%d", &t); E[t].push_back(node{i, j}); ma = max(ma, t); } for(int i = 1; i <= ma; i++) { for(int j = 1; j <= n; j++) Edge[j].clear(); int l = E[i].size(); for(int j = 0; j < l; j++) Edge[E[i][j].u].push_back(E[i][j].v); if(hungry() > k) ans.push_back(i); } int l = ans.size(); if(!l) printf("-1 "); else { for(int i = 0; i < l - 1; i++) printf("%d ", ans[i]); printf("%d ", ans[l - 1]); } } return 0; }