Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
一、题目描述:
给你一个 正 整数数组 beans ,其中每个整数表示一个袋子里装的魔法豆的数目。
请你从每个袋子中 拿出 一些豆子(也可以 不拿出),使得剩下的 非空 袋子中(即 至少 还有 一颗 魔法豆的袋子)魔法豆的数目 相等 。一旦魔法豆从袋子中取出,你不能将它放到任何其他的袋子中。
请你返回你需要拿出魔法豆的 最少数目。
示例
示例 1:
输入:beans = [4,1,6,5]
输出:4
解释:
- 我们从有 1 个魔法豆的袋子中拿出 1 颗魔法豆。
剩下袋子中魔法豆的数目为:[4,0,6,5]
- 然后我们从有 6 个魔法豆的袋子中拿出 2 个魔法豆。
剩下袋子中魔法豆的数目为:[4,0,4,5]
- 然后我们从有 5 个魔法豆的袋子中拿出 1 个魔法豆。
剩下袋子中魔法豆的数目为:[4,0,4,4]
总共拿出了 1 + 2 + 1 = 4 个魔法豆,剩下非空袋子中魔法豆的数目相等。
没有比取出 4 个魔法豆更少的方案。
示例 2:
输入:beans = [2,10,3,2]
输出:7
解释:
- 我们从有 2 个魔法豆的其中一个袋子中拿出 2 个魔法豆。
剩下袋子中魔法豆的数目为:[0,10,3,2]
- 然后我们从另一个有 2 个魔法豆的袋子中拿出 2 个魔法豆。
剩下袋子中魔法豆的数目为:[0,10,3,0]
- 然后我们从有 3 个魔法豆的袋子中拿出 3 个魔法豆。
剩下袋子中魔法豆的数目为:[0,10,0,0]
总共拿出了 2 + 2 + 3 = 7 个魔法豆,剩下非空袋子中魔法豆的数目相等。
没有比取出 7 个魔法豆更少的方案。
二、题解:
方法一 取出比较法
- 原理。每一种取出之和做比较。记录算过的值,算每一个数为留下的魔法豆的最小值即可
- 思路。
- 使用对象记录每一个留下的数,存在过则跳过,不存在且不为0则可以使用该值
- 判断数组其余数字和当前留下数差值是否小于0,小于0则全拿出,否则则拿出多的部分即可
代码:
var minimumRemoval = function(beans) {
// beans.sort((a,b) => a-b)
let min = 0
let obj ={}
for(let i=0;i<beans.length;i++){
let item = beans[i]
if(!obj[item] && item){
obj[item] = 1
let tempSum = 0
for(let j=0;j<beans.length;j++){
const _bean = beans[j]
if(!_bean || i===j){
continue
}
const diff = _bean-item
if(diff<0){
tempSum += _bean
} else if(diff>0){
tempSum += diff
}
}
if(!min){
min = tempSum
}else{
min = Math.min(tempSum,min)
}
}
}
return min
};
最后超时了,只能舍弃
方法二 留值最多法
- 原理。取出值最小,即求留下值最多。
- 思路。
- 先排序,从小到大的顺序
- 再求beans之和
- 因从小到大了,假定第i个袋子为最合适的留下值,
i之前则都不要,那么剩下的袋子都得为beans[i]的数目,即(beans.length-i)* beans[i]为留下最多的值 - 再使用 beans之和减去留下的最多值,即为最小留下的值
代码:
var minimumRemoval = function(beans) {
beans.sort((a,b) => a-b)
let total = beans.reduce((a,b) => a+b)
let min = total
for(let i=0;i<beans.length;i++){
min = Math.min(min, total-beans[i] * (beans.length-i) )
}
return min
};
方法三 留值最多法2
- 原理。取出值最小,即求留下值最多。
- 思路。
- 先使用函数得出最大值
- 构造一个长度为dp+1的数组dp(因为最大数的下标比他小一,所以长度+1)
- 将beans分布到dp上并且求和
- 再使用 beans之和减去留下的最多值,即为最小留下的值
代码:
var minimumRemoval = function(beans) {
const max = Math.max(...beans)
const dp = new Array(max+1).fill(0)
let _len = beans.length;
let total = 0
for(let item of beans){
dp[item]++
total+=item
}
let min = total
for(let i=1;i<dp.length;i++){
if(dp[i]>0){
min = Math.min(min, total-_len*i )
_len -= dp[i]
}
}
return min
};
这个地方的_len*i等同于方案2的(beans.length-i)* beans[i],_len为剩下比i大的长度(beans.length-i),i为beans[i]
具体可参考下图: 示例1的beans = [4,1,6,5]
方案比较
方案二 时间复杂度O(nlogn)
方案三 时间复杂度可能为O(k?)k为最大数(这个时间复杂度该怎么算呢?)
三、总结
- 此题可以取出比较法和留值最多法两种方案
- 取出比较法主要是每一种取出之和做比较。记录算过的值,算每一个数为留下的魔法豆的最小值即可。但由于这个题输入数据太多,算法时间复杂度过高,超出时间限制了
- 留值最多法取出值最小,即求留下值最多。
文中如有错误,欢迎在评论区指正