题意
升序输出不能用k次戳破的气球颜色编号, 若无输出 -1
题解
最小顶点覆盖:用最少的点,让每条边都至少和其中一个点关联;
我们只需要对于每种颜色判断一下该颜色的气球最少需要多少次才能全部扎破即可.
假设当前处理颜色为1的气球(注意气球颜色最多有50种).
我们把行标号放左点集,列标号放右点集合. 如果(i,j)格子的颜色为1,那么就连一条左i与右j点的边.
由于我们每次可以扎破一行或一列的所有同色气球,那么我们只需要选出尽量少的行号或列号(即左右点集中的点),然后看看这些行号或列号是否正好已经把所有的1颜色气球都覆盖了.
所以这就是一个典型的 二分图的最小顶点覆盖问题,
最小覆盖顶点数= 最大匹配数 |
代码
#include <bits/stdc++.h>
using namespace std;
#define rg register
#define sc scanf
#define pf printf
typedef long long ll;
class myHungary {
static const int MAXN = 1e2+10;
public:
int n;
int g[MAXN][MAXN];
bool vis[MAXN];
int link[MAXN];
int color;
myHungary ( ) { };
myHungary ( int _n ) : n(_n) { };
void ini ( int n ) {
this->n = n;
}
private:
bool match ( int u ) {
for ( int v = 1; v <= n; v++ )if ( g[u][v] == color && !vis[v] ) {
vis[v] = true;
if ( link[v] == -1 || match ( link[v] ) ) {
link[v] = u;
return true;
}
}
return false;
}
public:
int run ( int col ) {
color = col;
int ans = 0;
memset ( link, -1, sizeof ( link ) );
for ( int i = 1; i <= n; ++i ) {
memset ( vis, 0, sizeof ( vis ) );
if ( match( i ) ) ans++;
}
return ans;
}
}my;
int main ( ) {
int n, k;
set<int> st;
while ( scanf ( "%d%d", &n, &k ) == 2 && n ) {
st.clear();
my.ini ( n );
for ( int i = 1; i <= n; ++i ) {
for ( int j = 1; j <= n; j++ ) {
sc ( "%d", &my.g[i][j] );
st.insert ( my.g[i][j] );
}
}
int ans = 0; // 不能被全部扎破的气球种数
vector<int> notColor; // 不能被扎破气球的颜色
for ( set<int>::iterator it = st.begin( ); it != st.end( ); ++it ) {
if ( my.run( *it ) > k ) {
++ans;
notColor.push_back ( *it );
}
}
if ( notColor.size( )==0 ) pf ( "-1\n" );
else {
for ( int i = 0; i < notColor.size( )-1; ++i )
pf ( "%d ", notColor[i] );
pf ( "%d\n", notColor[notColor.size( )-1] );
}
}
return 0;
}