Caioj1149 牛场的婚礼

题目描述

【题目】
一个牛场有n只公牛,同时有n只母牛。
每只公牛都有自己喜欢的若干母牛。(一定存在完备匹配)
求每只公牛可以选择哪些母牛可以让剩下的每只公牛依旧能够选择到自己喜欢的一只母牛。
【输入格式】
第一行一个整数n(1 <= n <= 2000).
下来n行,每行第一个数ki表示第i只公牛喜欢的母牛数目,下来ki只母牛的编号。
所有ki的总和不超过 200000。

【输出格式】
   输出每只公牛可以匹配的母牛的编号(从小到大输出),且其他公牛和母牛依然一一匹配。
【样例输入】
4
2 1 2
2 1 2
2 2 3
2 3 4
【样例输出】
1 2
1 2
3
4

 

这可真是一道恶心的题目,虽然只用10多分钟就可以想出思路,但是打起代码发现非常困难

虽然题目是用强连通(其他同学上网%题解也是强连通),但是我并没有这样做

我是这样想的:

因为题目求的是”每只公牛可以选择哪些母牛可以让剩下的每只公牛依旧能够选择到自己喜欢的一只母牛“

所以一开始图中会存在某公牛是必定要和某母牛必然要匹配的情况,我们就把这些情况找出并记录和删除,然后根据这些情况不断的往下蔓延,知道没有这种情况的出现,然后图中剩余的公牛就是可以和自己连边中没有被访问过的母牛匹配(好复杂的一段话),那就举个例子(就是上面那个数据)

 

圆点表示公牛,方点表示母牛,点旁边的数字表示度及与该点相连的边的数量

当一个点的度变为1的时候,说明这一头牛已经有一个固定的配偶了

下面开始我们的搜索

方点4的度为1,说明母牛4的配偶是公牛4,然后将这两个点从图中删掉

在删去圆点4以后发现方点3的度变为1,说明母牛3的配偶是公牛3,又删除这两个点

最终剩余的点的度都变为2,不会再存在度为一的点了,搜索结束

于是公牛1可以和母牛1,2其中一头结婚,公牛2也可以选择母牛1,2中一头结婚

答案就出来了

但是做起来还是很麻烦的

参考代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>

using namespace std ;

const int N = 2e3 + 10 ;
const int M = 2e5 + 10 ;

struct edge { //边 
	int v , nxt ;
} e[M<<1] ; int tot , last[N<<1] ;
inline void add ( int u , int v ) {
	e[++tot] = (edge){ v , last[u] } ;
	last[u] = tot ;
	e[++tot] = (edge){ u , last[v] } ;
	last[v] = tot ;
}

int ans[N] ; //记录必定有唯一匹配的公牛 

int n , deep[N<<1] ; //deep表示度 
bool vis[N<<1] ; //vis表示是否访问过 

int main() {
	cin >> n ; int v , m ; 
	memset ( last , -1 , sizeof ( last )) , tot = -1 ;
	for ( int i = 1 ; i <= n ; i ++ ) {
		cin >> m ; 
		while ( m -- ) {
			cin >> v ; add ( i , v+n ) ;
			deep[i] ++ , deep[v+n] ++ ;
		}
	}
	memset ( vis , 1 , sizeof ( vis )) ; 
	while ( 1 ) {
		bool bk = false ; //判断是否有度为1的点 
		for ( int i = 1 ; i <= (n<<1) ; i ++ ) {
			if ( deep[i] == 1 && vis[i] == 1 ) { //如果没有搜过 
				bk = true ; vis[i] = 0 ; int x ; 
				for ( int j = last[i] ; j != -1 ; j = e[j].nxt ) { //找出这个唯一匹配的点 
					if ( vis[e[j].v] == 1 ) {
						x = e[j].v ; break ;
					}
				}
				if ( i <= n ) ans[i] = x ; //记录 
				else ans[x] = i ; vis[x] = 0 ; //并将这两个点和相连的边都删掉 
				for ( int j = last[x] ; j != -1 ; j = e[j].nxt ) {
					int v = e[j].v ; if ( vis[v] == 0 ) continue ;
					deep[v] -- ;
				}
			}
		}
		if ( bk == false ) break ;
	}
	int sta[N] , tp ;
	for ( int i = 1 ; i <= n ; i ++ ) { //输出 
		if ( vis[i] == 0 ) {
			printf ( "%d\n" , ans[i]-n ) ;
			continue ;
		}
		tp = 0 ;
		for ( int j = last[i] ; j != -1 ; j = e[j].nxt ) {
			int v = e[j].v ; if ( vis[v] == 0 ) continue ;
			sta[++tp] = v ;
		}
		sort ( sta + 1 , sta + tp + 1 ) ;
		for ( int j = 1 ; j <= tp ; j ++ ) printf ( "%d " , sta[j]-n ) ;
		putchar('\n') ;
	}
	return 0 ;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值