首页 > 其他分享 >「暴力」拿出最少数目的魔法豆(力扣第2171题)

「暴力」拿出最少数目的魔法豆(力扣第2171题)

时间:2024-01-18 16:15:43浏览次数:26  
标签:拿出 基准值 魔法 long 力扣 2171 beans 袋子

本题为1月18日力扣每日一题

题目来源:力扣第2171题

题目tag:数位dp 动态规划

题面

题目描述

给定一个正整数数组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 个魔法豆更少的方案。

提示

\(1 \leq beans.length \leq 10^5\)
\(1 \leq beans[i] \leq 10^5\)


思路分析

以前做过一道要求所有数相等求最少步数的题目,当时这题既可以增加也可以减少,是在转化问题后借助对顶堆这种数据结构来实现的,因此这题我的第一反应也是用同样的方式转化问题然后尝试解决,发现并没有什么用。

于是我开始思考是否能用其他数据结构,思考了许久也没有什么答案。然后我想能不能找到一种使得结果单调的方式,然后使用二分来解决问题。经过较长时间思考后我终于发现,这题只要直接暴力即可。

首先显然,最终统一化成的数(除了0)一定是给定数组中的某一个元素,这个性质在此处不做证明。那么,我只需要枚举每一个元素,然后分别计算需要取出的糖果的数目即可。这个计算很简单,大于选定元素的数,取出他们的差值即可;小于选定元素的数,由于不能加糖果,所以全部取出。但是这样是一个平方级别的复杂度,显然会超时,有没有办法优化呢?

由于我是从二分的角度入手思考的,所以我直接找到了解决的办法。对于正常的思考逻辑,我想可能可以这样理解:

  • 之所以直接枚举会使得复杂度达到平方,是因为在每次枚举一个数之后,我们一直在重复遍历整个数组来计算差值
  • 能否采取一种方式可以利用前一次计算的结果来得出当前的结果,省略掉重复的过程?
  • 因为这个结果实际上是差值,显然我每次调高或减小基准值时,每个大于等于新基准值的数所贡献的差值变化就是基准值的变化,因为 $(a - b) - (a - c) = c - b $ ;小于原本基准值的数的贡献不会改变,因为他们已经被减到0了;在两者之间的数(前闭后开)的贡献的变化就是原本的基准值,因为 \(a - (a - b) = b\)
  • 所以问题变成给定数组中的一个值,我能否快速知道数组中几个数比他小,几个数比他大。非常容易,我给数组排个序就好了。而且在排序后按顺序进行遍历时,你会发现上述所说的“在两者之间的数“其实只有前一次取的那个数,所以只需要直接将其从贡献中减掉即可

有了上面这个优化思路后,本题的做法非常容易。首先对数组进行一次排序,然后从小到大遍历整个数组,依次把每个数当成当前的基准值。每次的取出糖果数,就是上一次的取出糖果数,减去上一个基准值(介于两个基准值之间的数的贡献变化),然后加上当前元素及以后的元素个数乘上两次基准值的差(大于等于当前基准值的数的贡献变化)即可。这里注意计算第一个元素为基准值时的情况中,由于没有上一次取出糖果数,无法递推,因此只能直接遍历整个数组,依次计算差值。

注意此题的中间结果可能很大,会超过int的上限,需要开long long十年oi一场空,不开long long见祖宗。

参考代码

class Solution {
public:
    long long minimumRemoval(vector<int>& beans) {
        sort(beans.begin(),beans.end());

        // 第一个元素没有前一次的结果,直接计算
        long long cnt = 0;
        for(auto w : beans) {
            cnt += w - beans[0];
        }

        long long res = cnt;
        // 依次枚举每一项
        for(int i = 1;i < beans.size();i++){
            // 前一项变成0,贡献变化为前一项
            cnt += beans[i - 1];
            // 后面每项贡献变化为两次基准值的差值
            cnt -= 1LL * (beans[i] - beans[i - 1]) * (beans.size() - i);
            res = min(res,cnt);
        }

        return res;
    }
};

"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德

标签:拿出,基准值,魔法,long,力扣,2171,beans,袋子
From: https://www.cnblogs.com/geministar/p/-/LeetCode2171

相关文章

  • 贪心算法-题目3力扣53
    给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。输入:nums=[-2,1,-3,4,-1,2,1,-5,4]输出:6解释:连续子数组 [4,-1,2,1]的和最大,为 6。子数组 是数组中的一个连续部分。解题思路:从投......
  • 贪心算法-题目1力扣455(简单题)
    力扣455,给小朋友发饼干假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j]>=g[i],我们可以将这个饼干 j 分配......
  • 「数位dp」统计整数数目(力扣第2719题)
    本题为1月16日力扣每日一题题目来源:力扣第2719题题目tag:数位dp动态规划题面题目描述给你两个数字字符串num1和num2,以及两个整数max_sum和min_sum。如果一个整数x满足以下条件,我们称它是一个好整数:\(num1\leqx\leqnum2\)\(min\_sum\leqdigit\_sum(x)\leqmax\_s......
  • Docker 与 Linux Cgroups:资源隔离的魔法之旅
    这篇文章主要介绍了Docker如何利用Linux的ControlGroups(cgroups)实现容器的资源隔离和管理。最后通过简单Demo演示了如何使用Go和cgroups交互。如果你对云原生技术充满好奇,想要深入了解更多相关的文章和资讯,欢迎关注微信公众号。搜索公众号【探索云原生】即可订阅......
  • Python中的魔法方法
      Python中有很多魔法方法,它们以双下划线__开头和结尾,用于实现类的特殊行为。以下是一些常用的魔法方法:1.__init__(self,...)  初始化方法,用于创建对象并设置初始状态。2.__str__(self)  返回对象的非正式字符串表示形式,通过str()函数调用。3.__repr__(self)......
  • 20240113-力扣704二分查找
    leetcode链接:https://leetcode.cn/problems/binary-search/solutions/980494/er-fen-cha-zhao-by-leetcode-solution-f0xw/代码随想录链接:https://programmercarl.com/0704.二分查找.html#算法公开课考点:二分查找解决代码:classSolution{publicintsearch(int[]num......
  • 常用魔法方法和元类
    常用魔法方法和元类1.常用魔法方法__init__ :初始化类时触发__del__ :删除类时触发__new__ :构造类时触发__str__ :str函数或者print函数触发__repr__ :repr或者交互式解释器触发__doc__ :打印类内的注释内容__enter__ :打开文档触发__exit__ :关闭文档触发__getattr__:访......
  • Tauri魔法指南:从零开始开发一个桌面应用
    摘要:本文将以轻松幽默的笔调,带领读者探索如何使用Tauri这个前端魔法工具,从零开始开发一个跨平台的桌面应用。通过深刻的洞察和实际示例,让你轻松进入这个神奇的桌面开发领域。引言曾几何时,前端开发者们只是在浏览器中玩耍,创造着一个个网页的魔法。但如今,我们有了一个更大的舞台,一个......
  • 力扣11-盛最多水的容器
    难度:【中等】题目给画了图,比较方便理解。第一个思路是把所有的面积都计算一遍,显然时间复杂度很高;接着思考第二个方法,使用双指针,通过移动首尾指针来计算面积:如果下一个height超过当前值,就移动该指针,直到两个指针相遇。写完代码运行超时。超时是因为死循环了,因为上面的移动指针的......
  • 力扣543-二叉树的直径
    难度:【简单】定义:在一个二叉树中,任意两个节点之间的路径中最长的路径的长度称为其直径。路径长度由两个节点之间经过的“边”表示,而不是节点数。且二叉树的直径不一定经过根节点。先大致看了官方解法,不理解,心情暴躁没看懂,就自己瞎写。起初不理解直径不一定经过根节点。根据示......