并查集——糖果

题目描述

链接:https://ac.nowcoder.com/acm/contest/9983/G
来源:牛客网

在一个幼儿园里面有 n 个小朋友,分别编号 1,2,…,n。在这些小朋友中有一些小朋友互为朋友关系,总共有 m 对朋友。作为幼儿园老师,你想买一些糖果分给小朋友,你知道第 i 个小朋友想要至少 a[i] 个糖果,否则他就会不开心。同时,如果一个小朋友得到的糖果数小于他某个朋友得到的糖果数,他也会不开心。请问你最少买多少糖果才能保证每个小朋友都不会不开心呢?

输入描述

第一行以空格分隔的两个整数 n, m。
第二行以空格分隔的 n 个正整数 a[i]。
接下来 m 行每行以空格分隔的两个正整数 u, v,代表 u 是 v 的朋友,v 是 u 的朋友。
1 <= n <= 1e6
0 <= m <= 1e6
1 <= a[i] <= 1e9
1 <= u,v <= n, u != v

输出描述

购买的最少糖果数以保证每个小朋友都不会不开心。

解题思路

这道题需要注意的是当 a和b是朋友,b和c是朋友,此时a和b的糖果数相等,b和c的糖果数也相等,即a,b,c的糖果数都相等,所以实际上他们相互都是朋友。综上,我们可以将他们并入一个同一个集合,运用并查集的方法解题。

心得

并查集的应用相对广泛,可以配合下面我个人对并查集的理解和代码来进行学习,之后相关的题目的解题都基本相同!

并查集

并查集可以简单拆分为合并、查询集合。所谓合并就是当两个不同集合中的两个元素有关系时,我们可以将这两个集合合并为一个集合。而查询就是我们可以查询两个元素是否在同一个集合。并查集通常使用一个数组f表示每个元素的父元素,然后编写Union函数改变数组f中元素的值,若两个元素 a,b 有关系则通过改变f中a或b的值模拟合并。Find函数是查询元素的“最年老”的父元素,我们还可以对Find函数进行优化,让每个寻找过自己“最年老”的父元素的元素的f值直接等于其“最年老”的父元素。

C++代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
const int maxn = 1e6 + 5;

ll a[maxn], f[maxn];

ll Find(ll x) {
	//1. 当x的父元素不是x时就查找x的父元素的父元素 
	//return f[x] == x ? : Find(f[x]); 
	//2. 对 1 进行优化,让x的父元素直接等于其父元素的父元素,下次查找时则可减少查找次数 
	return f[x] == x ? x : f[x] = Find(f[x]);
}

void Union(ll x, ll y) {
	int fx = Find(x);
	int fy = Find(y);
	//如果两个元素的“最年老”父元素不相等则他们不在同一个集合,对他们进行合并 
	if(fx != fy) {
		f[fy] = fx;//注意这里直接改变fy的值 
		a[fx] = max(a[fx], a[fy]);
	}
}

int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
		f[i] = i;//首先让每个元素的父元素设为自身 
	}
	while(m--) {
		int u, v;
		scanf("%d%d", &u, &v);
		Union(u, v);
	}

	ll ans = 0;
	for(int i = 1; i <= n; i++) {
		ans += a[Find(i)];
	}
	printf("%lld", ans);
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个可能的实现: ```python import random def machine_play(num_candies): """ 人机版分糖果游戏:电脑用策略来选糖果 """ candy_choice = 0 if num_candies > 4: candy_choice = 4 - player_choice elif 2 <= num_candies <= 4: candy_choice = num_candies - 1 elif num_candies == 1: candy_choice = 1 print("电脑获胜!") else: # 这种情况是不应该出现的 print("错误:无效的糖果数") sys.exit() print(f"电脑选了{candy_choice}颗糖果") return candy_choice # 开始游戏 num_candies = 20 # 初始糖果数 player_choice = 0 # 上一轮选手选择的糖果数 while num_candies > 0: print(f"场上还有{num_candies}颗糖果") player_choice = int(input("请输入你要取走的糖果数:")) if player_choice < 1 or player_choice > 3 or player_choice >= num_candies: print("错误:无效的糖果数,请重新输入") continue num_candies -= player_choice if num_candies == 0: print("恭喜你获胜!") break candy_choice = machine_play(num_candies) num_candies -= candy_choice if num_candies == 0: print("电脑获胜!") break ``` 这个实现中,我们定义了一个 `machine_play` 函数,用于计算机的糖果选择。这个函数接受一个参数 `num_candies`,表示当前场上的糖果数,然后根据规则计算出计算机应该选择的糖果数,并将其返回。 在主循环中,我们首先让玩家输入糖果数,如果输入无效,则提示错误并重新让玩家输入。如果输入有效,则减去选手选择的糖果数,并检查场上剩下的糖果数是否为0。如果为0,则选手获胜,游戏结束。 如果场上剩下的糖果数不为0,则调用 `machine_play` 函数计算计算机的糖果选择,并减去计算机选择的糖果数,并检查场上剩下的糖果数是否为0。如果为0,则计算机获胜,游戏结束。如果还有剩余的糖果,则回到循环开始,让玩家继续输入糖果数。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kiefer_lin

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值