1561. 你可以获得的最大硬币数目
2020深信服笔试原题
有 3n 堆数目不一的硬币,你和你的朋友们打算按以下方式分硬币:
每一轮中,你将会选出 任意 3 堆硬币(不一定连续)。
Alice 将会取走硬币数量最多的那一堆。
你将会取走硬币数量第二多的那一堆。
Bob 将会取走最后一堆。
重复这个过程,直到没有更多硬币。
给你一个整数数组 piles ,其中 piles[i] 是第 i 堆中硬币的数目。
返回你可以获得的最大硬币数目。
示例 1:
输入:piles = [2,4,1,2,7,8]
输出:9
解释:选出 (2, 7, 8) ,Alice 取走 8 枚硬币的那堆,你取走 7 枚硬币的那堆,Bob 取走最后一堆。
选出 (1, 2, 4) , Alice 取走 4 枚硬币的那堆,你取走 2 枚硬币的那堆,Bob 取走最后一堆。
你可以获得的最大硬币数目:7 + 2 = 9.
考虑另外一种情况,如果选出的是 (1, 2, 8) 和 (2, 4, 7) ,你就只能得到 2 + 4 = 6 枚硬币,这不是最优解。
示例 2:
输入:piles = [2,4,5]
输出:4
示例 3:
输入:piles = [9,8,7,6,5,1,2,3,4]
输出:18
提示:
3 <= piles.length <= 10^5
piles.length % 3 == 0
1 <= piles[i] <= 10^4
思路:贪心算法
由于一共有 3n3n 堆硬币,每个人都会取走 nn 堆硬币,因此在取走的硬币堆的数量确定的情况下,为了获得最大硬币数目,在每次取走一堆硬币时,应取走数量最多的一堆硬币。
显然,\text{Alice}Alice 一定会取走 3n3n 堆硬币中数量最多的一堆硬币,在 \text{Alice}Alice 取走数量最多的一堆硬币之后,我们可以取走数量第二多的一堆硬币。当 n=1n=1 时,一共有 33 堆硬币,每个人都只能取走 11 堆硬币,因此我们可以获得的最大硬币数目即为数量第二多的一堆硬币的数目。
当 n \ge 2n≥2 时,如何获得最大硬币数目?和 n = 1n=1 的情况相似,第一轮时,\text{Alice}Alice 取走数量最多的,我们取走数量第二多的,但不同的是我们不让 \text{Bob}Bob 取走数量第三多的,我们让 \text{Bob}Bob 取走数量最少的。第二轮时,应该让 \text{Alice}Alice 取走数量第三多的一堆硬币,我们就取走数量第四多的一堆硬币,以此类推。试想如果我们再第一轮让 \text{Bob}Bob 拿走了第三多的,按照这个策略我们只能在第二轮拿到数量第五多的,以此类推,这个策略不如前面的策略好。
基于上述分析,可以看到,每一轮中,\text{Alice}Alice 取数量最多的一堆硬币,我们取数量第二多的一堆硬币,可以让我们获得最大硬币数目。由于每一轮中要选出 33 堆硬币,其中的最后一堆由 \text{Bob}Bob 取走,为了不让 \text{Bob}Bob 影响我们获得的最大硬币数目,只要让 \text{Bob}Bob 每次取的硬币是所有堆中数量最少的即可。
为了方便地知道每一堆硬币的数量之间的关系,首先对数组进行排序。排序后的数组的前 nn 个元素是最小的元素,留给 \text{Bob}Bob,其余的元素则分别属于我们和 \text{Alice}Alice。
每一轮,我们选出 33 堆硬币,包括数量最多的 22 堆硬币和数量最少的 11 堆硬币,我们总能获得这 33 堆硬币中的数量第二多的硬币。
计算可以获得的最大硬币数目时,按照从大到小的顺序遍历数组中的元素,每次遍历 22 个元素,其中较小的元素即为这一轮取走的硬币数量。
代码实现
public int maxCoins (int[] piles) {
if(piles.length==0 || piles==null) return 0;
Arrays.sort(piles);
if(piles.length < 3) {
return piles[0];
}
int len = piles.length;
int rounds = len / 3;
int coins = 0;
int idx = len-2;
for (int i = 0; i < rounds; i++) {
coins += piles[idx];
idx -= 2;
}
return coins;
}