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

这篇具有很好参考价值的文章主要介绍了「暴力」拿出最少数目的魔法豆(力扣第2171题)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本题为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;
    }
};

"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德文章来源地址https://www.toymoban.com/news/detail-801696.html

到了这里,关于「暴力」拿出最少数目的魔法豆(力扣第2171题)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 力扣第738题 单调递增的数字 c++ 暴力超时 贪心优化

    738. 单调递增的数字 中等 相关标签 贪心  数学 当且仅当每个相邻位数上的数字  x  和  y  满足  x = y  时,我们称这个整数是 单调递增 的。 给定一个整数  n  ,返回  小于或等于  n  的最大数字,且数字呈  单调递增  。 示例 1: 示例 2: 示例 3: 提示: 0 = n = 109 从N开始

    2024年02月08日
    浏览(37)
  • 力扣第55题 跳跃游戏 c++ 贪心 + 覆盖 加暴力超时参考

    55. 跳跃游戏 中等 相关标签 贪心   数组   动态规划 给你一个非负整数数组  nums  ,你最初位于数组的  第一个下标  。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标,如果可以,返回  true  ;否则,返回  false  。 示例 1:

    2024年02月06日
    浏览(54)
  • 力扣第501题 二叉树的众数 c++ (暴力 加 双指针优化)

    501. 二叉搜索树中的众数 简单 相关标签 树   深度优先搜索   二叉搜索树   二叉树 给你一个含重复值的二叉搜索树(BST)的根节点  root  ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。 如果树中有不止一个众数,可以按  任意顺序  返回。 假定 BST 满足

    2024年02月07日
    浏览(38)
  • 力扣第34天----第416题

    ​ 完全懵掉,明天再复习。晕乎乎~~~

    2024年02月09日
    浏览(22)
  • 力扣第357场周赛补题

    6925. 故障键盘 - 力扣(LeetCode) 思路:模拟 6953. 判断是否能拆分数组 - 力扣(LeetCode) 思路:脑筋急转弯 2812. 找出最安全路径 - 力扣(LeetCode) 思路:多源bfs+倒序枚举+并查集

    2024年02月14日
    浏览(37)
  • 【算法】力扣第 284 场周赛(最短代码)

    看数据范围 1 = nums.length = 1000 ,直接暴力 2行 搞定 看数据范围 1 = n = 1000 ,每个工件最多只覆盖4个单元格,直接哈希+暴力, 2行搞定 这题比较吃细节,推荐大家看一下灵茶山艾府大佬的题解, 1行 就搞定了 三次dijkstra,可可也是看了题解之后才做出来, 15行 解法👇 T4罚坐一

    2024年02月13日
    浏览(45)
  • 力扣第40题 组合总和 || c++ 回溯经典

    40. 组合总和 II 中等 相关标签 数组   回溯 给定一个候选人编号的集合  candidates  和一个目标数  target  ,找出  candidates  中所有可以使数字和为  target  的组合。 candidates  中的每个数字在每个组合中只能使用  一次  。 注意: 解集不能包含重复的组合。  示例 1: 示例

    2024年02月07日
    浏览(40)
  • 「数位dp」统计整数数目(力扣第2719题)

    本题为1月16日力扣每日一题 题目来源:力扣第2719题 题目tag: 数位dp 动态规划 给你两个数字字符串num1和num2,以及两个整数max_sum和min_sum。如果一个整数x满足以下条件,我们称它是一个好整数: (num1 leq x leq num2) (min_sum leq digit_sum(x) leq max_sum) 请你返回好整数的数目。答案

    2024年01月16日
    浏览(38)
  • 力扣第474题 一和零 c++ 动态规划 01背包

    474. 一和零 中等 相关标签 数组   字符串   动态规划 给你一个二进制字符串数组  strs  和两个整数  m  和  n  。 请你找出并返回  strs  的最大子集的长度,该子集中  最多  有  m  个  0  和  n  个  1  。 如果  x  的所有元素也是  y  的元素,集合  x  是集合  y

    2024年02月06日
    浏览(37)
  • 力扣第92题——反转链表 II(C语言题解)

    给你单链表的头指针  head  和两个整数  left  和  right  ,其中  left = right  。请你反转从位置  left  到位置  right  的链表节点,返回  反转后的链表  。 示例 1: 示例 2: 提示: 链表中节点数目为  n 1 = n = 500 -500 = Node.val = 500 1 = left = right = n 我们以下图中黄色区域的链

    2024年01月23日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包