【教练】题解

Link


一、考试的情况

看完题基本是立马有了思路,然后开始打代码,写了 10 min ⁡ 10 \min 10min 左右就过了所有样例,就直接放下了。提交时看到只有 80 80 80 分,当时也没怎么留意,觉得肯定有点问题。但郭老师讲这道题的时候随意看了一下自己的代码,发现:

心态崩掉。


二、分析

题目要求要使每个人都在长度为 3 3 3 的队伍中,不能有其他情况。可以使用并查集维护每个团队。

当输入完后,就可以提前算出一部分可以确定的组队方案。此时,若已有团队的长度大于 3 3 3,则肯定不行。(这是一个小优化)

但现在,还剩下一些不确定的人需要互相组合,设当前元素为 i i i,所在集合的长度为 s i z e i size_i sizei。那么有 3 3 3 种情况:

  • s i z e i = 3 size_i=3 sizei=3。即已经完成组队,不处理。
  • s i z e i = 2 size_i=2 sizei=2。此时只需要再来一个单独的人 j j j s i z e j = 1 size_j=1 sizej=1)与之匹配,这样它们加起来长度就是 2 + 1 = 3 2+1=3 2+1=3,即完成组队。在 [ 1 , n ] [1,n] [1,n] 枚举满足条件的 j j j 即可。处理完后,若还剩下 s i z e i = 2 size_i=2 sizei=2 的情况,说明没有足够的 s i z e j size_j sizej=1 与 i i i 组队,那么肯定不行。(这又是一个小优化)
  • s i z e i = 1 size_i=1 sizei=1。此时又分为 2 2 2 种情况。
    • 找另一个 j j j s i z e j = 2 size_j=2 sizej=2)。但因为上面已经处理了 s i z e i = 2 size_i=2 sizei=2 的情况,也就意味着 s i z e i = 2 size_i=2 sizei=2 的人已经确定了。所以现在肯定没有 s i z e j = 2 size_j=2 sizej=2 能与之匹配。
    • 再找两个 s i z e j = 1 size_j=1 sizej=1。实际上就是把所有的 1 1 1 合并。因为 i i i 是从小到大,则前面 [ 1 , i − 1 ] [1,i-1] [1,i1] 都被扫描过,所以它们最终都会变为长度为 3 3 3 的团队,不可能与 i i i 匹配。那么直接在 [ i + 1 , n ] [i+1,n] [i+1,n] 枚举 j j j 即可。

将所有不确定的人组完队后,我们只需要判断 i i i 所在的团队长度是否为 3 3 3,若不,则说明无论如何都不能完成组队了。

输出时,对于每个 i i i,将 i i i 所在的集合的所有元素直接输出。用一个 bool 型数组动态维护 i i i 是否输出过即可。


三、代码

for(int i = 1;i <= m; ++i) {
	scanf("%d %d", &a, &b);
	Union_Set(a, b);
	if(size[Find_Set(b)] > 3) {
		puts("-1");
		return 0;
	}
}
for(int i = 1;i <= n; ++i) 
	if(size[Find_Set(i)] == 2) 
		for(int j = 1;j <= n; ++j) 
			if(size[Find_Set(j)] == 1) {
				Union_Set(i, j);
				break;
			}
for(int i = 1;i <= n; ++i) 
	if(size[Find_Set(i)] == 1) 
		for(int j = i + 1;j <= n; ++j) {
			if(size[Find_Set(j)] == 1) Union_Set(i, j);
			if(size[Find_Set(i)] == 3) break; 
		}
for(int i = 1;i <= n; ++i) 
	if(size[Find_Set(i)] != 3) {
		puts("-1");
		return 0;
	} 
for(int i = 1;i <= n; ++i)
	if(!flag[i]) {
		printf("%d ", i);
		for(int j = i + 1;j <= n; ++j)
			if(Find_Set(i) == Find_Set(j) and !flag[j]) {
				printf("%d ", j);
				flag[j] = 1;
			}
		puts("");
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值