HDU 1498 50 years, 50 colors(二分图最小覆盖数)
http://acm.hdu.edu.cn/showproblem.php?pid=1498
题意:
给你一个n*n的矩阵,在矩阵中分布着s种颜色的气球,给你k次扎破气球的操作,每次操作可以扎破一行,或一列的同一颜色的气球。问在k次操作后有那几种颜色的气球是不能被完全扎破的.
分析:
其实本问题很类似于POJ3041:
http://blog.csdn.net/u013480600/article/details/38615197我们只需要对于每种颜色判断一下该颜色的气球最少需要多少次才能全部扎破即可.
假设当前处理颜色为1的气球(注意气球颜色最多有50种). 我们把行标号放左点集,列标号放右点集合. 如果(i,j)格子的颜色为1,那么就连一条左i与右j点的边.
由于我们每次可以消灭一行或一列的所有同色气球,那么我们只需要选出尽量少的行号或列号(即左右点集中的点),然后看看这些行号或列号是否正好已经把所有的1颜色气球都覆盖了.
上述问题就是二分图的最小覆盖数问题(想想是不是). 最小覆盖数= 最大匹配数.
AC代码:
#include<cstdio> #include<cstring> #include<set> #include<vector> using namespace std; const int maxn = 100+5; struct Max_Match { int n; int g[maxn][maxn]; bool vis[maxn]; int left[maxn]; int color; void init(int n) { this->n=n; } bool match(int u) { for(int v=1;v<=n;v++)if(g[u][v]==color && !vis[v]) { vis[v]=true; if(left[v]==-1 || match(left[v])) { left[v]=u; return true; } } return false; } int solve(int c) { color=c; int ans=0; memset(left,-1,sizeof(left)); for(int i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); if(match(i)) ans++; } return ans; } }MM; int main() { int n,k; while(scanf("%d%d",&n,&k)==2 && n) { set<int> color; MM.init(n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { scanf("%d",&MM.g[i][j]); color.insert(MM.g[i][j]); } int ans=0; //不能被全部扎破的气球种数 vector<int> col;//保存不能被扎破气球的颜色 for(set<int>::iterator it = color.begin(); it!= color.end(); ++it) { if(MM.solve(*it)>k) { ++ans; col.push_back(*it); } } if(col.size()==0) printf("-1\n"); else { for(int i=0;i<col.size()-1;i++) printf("%d ",col[i]); printf("%d\n",col[col.size()-1]); } } return 0; }