LeetCode 781. Rabbits in Forest

@(LeetCode)
问题描述
在一个森林里,每只兔子有着某种颜色。部分兔子(也可能是全部)会告诉你额外还有多少只与其颜色一样的兔子。这些答案放在数组中。
返回森林中可能存在的最少数量的兔子。
栗 1:
栗 2:
栗 3:
注意:
answers最大长度为1000。每个答案
answer[i]都是整数,且范围在[0, 999]。
解题思路
解这道题最主要是想清楚在什么条件下,会让兔子数量最少。
我们先来看几个栗子。
栗子分析
栗 1
举个简单的栗子,比如 answers = [1, 1]。
第一只兔子说与其颜色相同的有
1只,假设颜色为A,那么A色的总共有2只兔子。第二只兔子说与其颜色相同的有
1只,假设颜色为B,那么B色的总共有2只兔子。
试想一下:
如果
A与B不同,那么总共有2 + 2 = 4只兔子。如果
A与B相同,那么总共有2只兔子。
因此,从上可以看出,要想让兔子数量最小,就要尽量让兔子同色,同时也要满足兔子所说的同色个数。
栗 2
另外再举个栗子,比如 answers = [1, 1, 1]。
前两只兔子的分析同栗 1,假设颜色均为 A,且有 2 只。
第三只兔子说与其颜色相同的兔子有 1 只,那么它可能为 A 色吗?
答案是不可能,因为如果为 A,则三只兔子同色,与它们所说的数量不同。所以它只能是另外的颜色,且有 2 只。
因此,最少兔子数量为 2 + 2 = 4。
栗 3
再看个稍微复杂些的例子,比如 answers = [5, 2, 2, 0, 2, 2, 2]。分析过程如下:
第一只兔子说有
5只与其同色,假设颜色为A,那么A色共有6只兔子。第二只兔子说有
2只与其同色。
虽然我们上面说过,要让兔子尽可能同色,但它可能与 A 同色吗?
很显然不能。如果同色,就不满足兔 1 说的条件,互相矛盾。所以兔 2 是另外的颜色,记为 B,总数为 3。B 的剩余坑位为 2。
第三只兔子说有
2只与其同色,而跟它口径一致的是兔2,那么他们可能同色吗?可能,因为B的剩余坑位数还有2。它占了一个后,B的剩余坑位为1。第四只兔子说没有与其同色的,那么它只能是单独的颜色,总数为
1。第五只兔子说有
2只兔子同色,而跟它口径一致的是兔3,它的颜色是B,坑位还有1个,因此兔5可以是B。此时,B的剩余坑位为0。第六只兔子说有
2只兔子同色,跟它口径一致的是兔3,而颜色B的坑位已经被占完。因此,它只能是其他的颜色,记为C,总数为3。C的剩余坑位为2。第七只兔子说有
2只兔子同色,虽然跟它口径一致的是兔3和6,但兔3的颜色B坑位已经被占完,所以只能占C的坑位。
因此,最少兔子数量为 6 + 3 + 1 + 3 = 13。
从上面几个栗子,我们可以总结出如下结论:
尽量让兔子同色,这样才能让数量最少。
只有答案数量相同的兔子才可能为同色,否则说法矛盾。
当同色的坑被占完后,同样答案的兔子就不能是同色了,只能新起颜色。
每次新起颜色,兔子总数需要加上
answer + 1。
初始解法
我最初的解法想的有些复杂:动态地给兔子分配颜色,颜色值为从 1 开始不断递增,并记录该颜色对应的总个数(等于 answer + 1)和剩余坑位数(也就等于 answer)。
在处理某只兔子的 answer 时,从 颜色 1 开始逐个遍历已分配的颜色,如果颜色对应的总个数与 answer + 1 相等,即它们的答案相同,则说明可以是同色。
当剩余坑位足够,则坑位数减
1。当剩余坑位不足,则分配新的颜色。
最终计算所有出颜色所对应的兔子总数之和。
数据结构如下所示,兔子总数为累加 totalCount 的值。
但这种解法的效率不高,因为每次都需要去遍历已分配的颜色。提交后运行速度只超过了 12% 的 js 代码。
关键代码如下:
优化解法
其实,仔细想一想,我们并不需要给兔子分配颜色,只需要把同样答案的兔子聚集在一起,然后再逐个处理每个答案对应的数组。如下所示:
比如 [2, 2, 2, 2, 2],兔子总数为 sum = 0。
第一只兔子说有 2 只与其同色,那么同色总共有 3 个,sum += 3,此时还剩余 2 个坑位。
从第二只兔子开始遍历,如果坑位够,则坑位 - 1;如果不够,则新起颜色,sum += 3。
...
以此类推。
因此,总是在新起颜色的时候,更新兔子总数为 sum += answer + 1,也就是上面的结论 4。
这种方式比解法 1 要高效,68%。
关键代码如下:
最优解
其实第二种已经比较靠近最优解了,但是没有必要去一个个遍历数组计算兔子数量,这样是手工在计算数量。其实将相同答案的兔子聚合后,可以根据答案对应的兔子数量 count 来计算出所需的兔子数。
为什么这样说呢?通过解法 2 也能略知一二,因为它就是在模拟整个计算过程。下面我们还是来分析一下。
首先明确一点,答案的数值代表同色兔子数目为 answer + 1,显然,它可以覆盖到 answer + 1 只兔子。
当坑位不足的时候,即从 answer + 2 只兔子开始,需要新起颜色,此时又可以向后覆盖到 answer + 1 只兔子。因此,我们只需要计算出需要几组坑位 n,才能覆盖所有的兔子。而每组的坑位数为 answer + 1,那么数量为 n * (answer + 1)。
这么说可能还是不大清楚,来看看下面几个栗子。
假设答案为
2的兔子有5只,此时某种颜色的坑位数为3,可以覆盖前3只兔子。第4只兔子需新起颜色,可覆盖后2只兔子,因此需要2组坑位。
假设答案为
2的兔子有3只,此时某种颜色的坑位数为3,现在刚好有3只兔子,可以满足,所以只需1组坑位。
假设答案为
2的兔子有6只,此时某种颜色的坑位数为3,可以覆盖前3只兔子。第4只兔子需新起颜色,覆盖后三只兔子,则需要2组坑位。
从上,可以推导出:
如果
count / (answer + 1) == 0,坑位组数 =count / (answer + 1)。如果
count / (answer + 1) != 0,坑位组数 =count / (answer + 1) + 1。
这种方式是最简洁的,但是需要理解其思想。效率也最高,96%。
本文同步发表在简书:https://www.jianshu.com/p/d3efd992a07a。
版权声明: 本文为 InfoQ 作者【liu_liu】的原创文章。
原文链接:【http://xie.infoq.cn/article/0badd8de1ae22c261eb1ab265】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论