题意:给你一个n*n的矩阵,在矩阵中分布着s种颜色的气球,给你k次扎破气球的操作,每次操作可以扎破一行,或一列的同一颜色的气球。问在k次操作后有那几种颜色的气球是不能被完全扎破的.
思路:只需要对于每种颜色判断一下该颜色的气球最少需要多少次才能全部扎破即可.
假设当前处理颜色为1的气球(注意气球颜色最多有50种). 我们把行标号放左点集,列标号放右点集合. 如果(i,j)格子的颜色为1,那么就连一条左i与右j点的边.
由于我们每次可以消灭一行或一列的所有同色气球,那么我们只需要选出尽量少的行号或列号(即左右点集中的点),然后看看这些行号或列号是否正好已经把所有的1颜色气球都覆盖了.
上述问题就是二分图的最小覆盖数问题. 最小覆盖数= 最大匹配数.
#include<cstdio>
#include<cstring>
#include<set>
#include<vector>
using namespace std;
const int maxn=100+5;
struct Max_Match
{
int n,m;//左右点集大小,点从1开始编号
int g[maxn][maxn];//g[i]表示左边第i个点邻接的右边点的集合
bool vis[maxn];//vis[i]表示右边第i个点是否在本次match中被访问过
int left[maxn];//left[i]==j表右边第i个点与左边第j个点匹配,为-1表无点匹配
int color;
void init(int n)
{
this->n=n;
// this->m=m;
// for(int i=1;i<=n;i++) g[i].clear();
memset(left,-1,sizeof(left));
}
//判断从左u点是否可以找到一条增广路
bool match(int u)
{
for(int v=1;v<=n;v++)
{
if(!vis[v] && g[u][v]==color)
{
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;//最大匹配数
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)!=EOF && 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++)
{
memset(MM.left,-1,sizeof(MM.left));
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]);
}
}
}