HDU_1498 50years50colors( 二分图最大匹配 | 最小顶点覆盖 )

文章目录

题目连接 hdu or vj

题意

升序输出不能用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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值