题目链接 :http://acm.hdu.edu.cn/showproblem.php?pid=1498
题目大意:给出一个n*n的矩阵,矩阵每个元素代表一个气球的颜色。颜色取值范围是[1,50]。最多能进行k次操作,每次操作可以打掉某一行同种颜色的气球或者打掉某一列同种颜色的气球。问对于每一种颜色的气球经过k次操作是否能够消除完。
算法:首先,我们只考虑一种颜色的消除情况。
对于样例2
2 1
1 1
1 2
比如我们只考虑颜色1
那么其中的邻接矩阵应该为
1 1
1 0
对于某个气球,它的坐标为(x,y)
要消除它只需要操作x行或者y列,显然没必要同时进行这两个操作
同样的,我们建立二分图的模型
X集合为所有行号,Y集合为所有列号,每个坐标为一条边。
那么最后要求的是一个顶点集合,这个顶点集合要满足,所有边至少有一个顶点在这个顶点集合中,而且这个顶点集合要最小。其实这就是最小顶点覆盖,在二分图中最小顶点覆盖就等于最大匹配。
解决了最主要的问题,那么接下来的问题就轻松了,只要枚举每种颜色就行了,对于每一种颜色算出对应的最大匹配,如果大于k,这说明这种颜色经过k次操作是不可能消除完的,则把这个颜色记录在答案集合中去。
最后判断如果答案集合元素个数为0,表示每种出现过的颜色都可以被消除完。
否则排序之后输出答案。
下面是AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=105;
int Map[maxn][maxn],vis[maxn],girl[maxn],n,k;
int Find(int x,int color)
{
for(int i=1;i<=n;i++)
{
if(!vis[i]&&Map[x][i]==color)
{
vis[i]=1;
if(!girl[i]||Find(girl[i],color))
{
girl[i]=x;
return true;
}
}
}
return false;
}
int match(int color)
{
int ans=0;
memset(girl,0,sizeof(girl));
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof(vis));
if(Find(i,color)) ans++;
}
return ans;
}
int main()
{
while(~scanf("%d%d",&n,&k),n+k)
{
//memset(Map,0,sizeof(Map));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&Map[i][j]);//读入颜色矩阵
vector<int>ans;//存最后不能被全部消除的颜色
for(int i=1;i<=50;i++)//枚举每一种颜色
if(match(i)>k) ans.push_back(i);//如果有颜色根本没出现过,那么它算出的最大匹配必为0显然会小于k
if(ans.size()==0) printf("-1\n");
else
{
sort(ans.begin(),ans.end());//排序,其实,也没必要排序,因为我们是从小到大枚举的,那么答案势必从小到大加进去的,肯定是有序的
for(int i=0;i<ans.size()-1;i++) printf("%d ",ans[i]);
printf("%d\n",ans[ans.size()-1]);
}
}
return 0;
}