首页 > 其他分享 >【LeetCode 1648】销售价值减少的颜色球

【LeetCode 1648】销售价值减少的颜色球

时间:2024-04-30 12:55:46浏览次数:18  
标签:颜色 int max 1648 LeetCode nextMax inventory maxCnt orders

题目描述

原题链接: LeetCode.1648 销售价值减少的颜色球

解题思路

  • 题意很容易理解, 就是每次挑剩余同色球数量最大的颜色卖得到最大价值, 总共卖orders个球的最大总价值;
  • 最快速直观暴力的解法是按照同色球数量排序, 每次取数量最大值累加到总价值中并且将数量减一后重新排序, 重复orders次即可。 第一反应是用堆排序求解, 堆顶是最大同色球数量, 每次堆顶减一后调整堆;
  • 稍微用数组模拟一下就发现不需要堆排序将问题复杂化, 每次减一累加价值的解法效率太低, 进而想到基于排序后数据的一种快速解法:
    • 将同色球数量从大到小排序后, 取当前最大值max以及第二大的值nextMax, 其中剩余同色球数量为max的颜色可能有maxCnt种;
    • 在将所有max数量的同色球都卖到只剩nextMax之前, 最大价值都是逐个颜色将这些颜色的球卖出得到的, 即每种颜色都卖(max - nextMax)个得到累计 \(\sum_{i=nextMax+1}^{max} i\) * maxCnt 的价值;
    • 如果将这批同色球卖到高于nextMax的某个值就已经卖足orders个, 假设maxCnt中有a种颜色球卖到finalMax, b种球卖到finalMax-1个, 这一轮卖出得到的价值之和为 \(\sum_{i=finalMax}^{max} i\) * maxCnt + b * finalMax;
    • 如果卖到nextMax仍然没有卖够orders个, max更新为上一轮nextMax的值, 再向后遍历有序数组得到新的nextMax, 重复累加卖出价值的步骤;
    • 排序时间复杂度是 O(n \(log_2 n\)), 后续循环遍历最坏情况下max和nextMax都是只相差1时性能降到O(n), 所以整体时间复杂度是 O(n + n \(log_2 n\));
  • 上述思路代码提交后时间只击败了60%的用户, 显然并不高效, 看了眼其他人的示例代码才发现可以用二分法直接找到finalMax:
    • 根据第一种思路模拟过程可以得到的结论是, 所有初始数量超过finalMax的颜色球卖到剩余finalMax或者finalMax-1个时能卖足orders个;
    • 即必定存在唯一的finalMax值能满足\(\sum_{i=0}^{n-1} {Math.max(inv_i - finalMax, 0)}\) <= orders < \(\sum_{i=0}^{n-1} {Math.max(inv_i - finalMax + 1, 0)}\);
    • 时间复杂度是 O(n \(log_2 M\)), 其中M是初始数量最大值;
  • 由于初始数组长度不超过\(10^5\)但是数组元素最大值可以达到\(10^9\), 严格从时间复杂度角度来分析理论上二分查找的效率并不会高于排序解法, 但是提交结果显示二分法是最优的, 对此只能说数据用例倾向于二分查找的解法, 我自己也确实是因为提交发现性能不是最优才细看发现能用二分查找解决的, 学到了;

解题代码

  • 排序模拟售出的思路:

      final int MOD = 1_000_000_007;
      /**
       * 从大到小排序后,逐个计算卖出最高价值那批球的利润,等差递减序列求和的时候注意取余
       * 执行用时: 26 ms , 在所有 Java 提交中击败了 61.54% 的用户
       * 内存消耗: 53.91 MB , 在所有 Java 提交中击败了 12.82% 的用户
       */
      public int maxProfit(int[] inventory, int orders) {
          int len = inventory.length;
          if (len == 1) {
              return (int) (((long) orders * (inventory[0] + inventory[0] - orders + 1) >> 1) % MOD);
          }
          // 懒得自己写从大到小排序的自定义代码, 直接用API排序后再翻转数组实现了
          Arrays.sort(inventory);
          for (int i = 0; i < (len >> 1); i++) {
              int tmp = inventory[i];
              inventory[i] = inventory[len - 1 - i];
              inventory[len - 1 - i] = tmp;
          }
          long ans = 0;
          // 当前剩余数量最多(即卖出价值最高)的颜色球价值, maxCnt是价值最高颜色球的种数
          int max = inventory[0], maxCnt = 1;
          while (orders > 0) {
              while (maxCnt < len && inventory[maxCnt] == max) {
                  maxCnt++;
              }
              int nextMax = maxCnt < len ? inventory[maxCnt] : 0;
              int diff = orders / maxCnt;
              if (diff == 0) {
                  ans = (ans + (long) orders * max) % MOD;
                  break;
              }
              if (diff > max - nextMax) {
                  diff = max - nextMax;
              }
              // 从这maxCnt剩余数量为max的球中各自卖出diff个的总价值
              long profit = (maxCnt * ((((long) max + max - diff + 1) * diff) >> 1 % MOD)) % MOD;
              max = max - diff;
              ans = (ans + profit) % MOD;
              orders -= diff * maxCnt;
          }
          return (int) ans;
      }
    
  • 二分查找的解法:

      /**
       * 二分查找确定卖完后的最大数量
       * 执行用时: 13 ms , 在所有 Java 提交中击败了 94.87% 的用户
       * 内存消耗: 53.84 MB , 在所有 Java 提交中击败了 43.59% 的用户
       */
      public int maxProfit(int[] inventory, int orders) {
          int max = 0;
          for (int i : inventory) {
              if (i > max) {
                  max = i;
              }
          }
          int left = 0, right = max, t = 0;
          long tmpCnt = 0;
          while (left <= right) {
              int mid = (left + right) >> 1;
              // 数量超过mid的球卖到剩余mid时的数量之和是否超过orders
              long cnt = 0;
              for (int i : inventory) {
                  if (i > mid) {
                      cnt += (i - mid);
                      // 不必每次都遍历完整数组求和, 提前break提升效率
                      if (cnt > orders) {
                          break;
                      }
                  }
              }
              if (cnt <= orders) {
                  t = mid;
                  tmpCnt = cnt;
                  right = mid - 1;
              }
              else {
                  left = mid + 1;
              }
          }
          long ans = 0;
          for (int i : inventory) {
              if (i <= t) {
                  continue;
              }
              // 等差数列求和公式计算数量从i递减到t的价值之和
              ans = (ans + (((long) i - t) * (t + 1 + i) >> 1)) % MOD;
          }
          if (orders > tmpCnt) {// 还需要在卖到剩余t个的球中继续将部分颜色球依次卖1个才能满足orders值
              ans = (ans + ((long) orders - tmpCnt) * t) % MOD;
          }
          return (int) ans;
      }
    

标签:颜色,int,max,1648,LeetCode,nextMax,inventory,maxCnt,orders
From: https://www.cnblogs.com/coding-memory/p/18167833

相关文章

  • 获取给定区域内的符合颜色值的第一个和最后一个坐标
    fromPILimportImageGrabimportpyautoguiimporttimeimportpyperclipimportnumpydef获取给定区域内的符合颜色值的第一个和最后一个坐标(left_x:int,left_y:int,right_x:int,right_y:int,color_r:int,color_g:int,color_b:int)->list:'''注意,本函数直接截取......
  • png图片更换前景颜色
    png图片更换前景颜色#regionpng图片转换前景色///<summary>///获取原图的像素点颜色值,修改颜色值///</summary>///<paramname="bmp">原图</param>///<returns>修改原图的像素点颜色值之后的图</returns>publicstaticBitmapSetBitmapColor(Bitmapbmp,Color......
  • EPAI手绘建模APP颜色、贴图、材质、样式
    ⑦ 颜色选择页面1) 颜色环选色。图 65 颜色选择器-颜色环2) RGB选色。图 66 颜色选择器-RGB3) HSL选色。图 67 颜色选择器-HSL4) 国风颜色库选色。图 68 颜色选择器-国风5) CSS颜色库选色。图 69 颜色选择器-CSS6) 历史颜色:保存最近使用的多个颜......
  • leetcode(力扣) 2866. 美丽塔 II
    原题链接暴力做法(时间复杂度O(n^2))每次选取下标i为峰值,进行n次,对每次取max就可以找打答案对于i左边的序列:需要满足序列是非递减的,同时每个值尽可能大所以满足:下标为j的位置上的数<=下标是(j,i]的最小的值(等于时取得最大值),同时需要保证j位......
  • blender python api 将指定的顶点组(water)转换为颜色属性water_colors
    1.选中物体,进入权重绘制模式2.代码:importbpy#获取当前活动的物体obj=bpy.context.object#确保物体是网格类型ifobj.type!='MESH':print("当前激活的对象不是网格类型。")#exit()#使用exit()来提前退出脚本#获取名为“water”的顶点组vertex_gro......
  • blender python api 获取所有顶点组并将各自的顶点组转换为对应的颜色属性
    1.选中物体,进入权重绘制模式2.代码importbpy#获取当前活动的物体obj=bpy.context.object#确保物体是网格类型ifobj.type!='MESH':print("当前激活的对象不是网格类型。")#exit()#遍历所有顶点组forvg_nameinobj.vertex_groups.keys():#获......
  • android更改EditText下划线颜色
    在res——》values——》themes中添加下列代码<stylename="editTextStyle"><!--选中时下划线的颜色--><itemname="colorControlActivated">@color/gray1</item><!--默认时下划线的颜色--><itemname="colorControlNormal"&......
  • LeetCode三则
    72.编辑距离给你两个单词word1和word2,请返回将word1转换成word2所使用的最少操作数。你可以对一个单词进行如下三种操作:插入一个字符删除一个字符替换一个字符示例1:输入:word1="horse",word2="ros"输出:3解释:horse->rorse(将'h'替换为'r')rorse->rose(删......
  • 更改Android Studio的按钮演颜色
    并不是指通过background去更改,是另一种改前: 改后: 步骤:进入res——》values——》themes.xml 将 改成 就可以啦   参考——https://blog.csdn.net/qq_44995275/article/details/134699448......
  • 再次修复combobox下拉背景颜色
    2023年2月写的修复lazaruscombobox的下拉列表在linux时没有高亮显示选中的item的问题,需然解决了显示问题,但下拉列表的颜色在银河麒麟是灰黑色,和应用的颜色明显不搭,想要win一样样式,如果要改变下拉背景颜色,可以按以下修改就可以,当然,如果不想用白色,可以改为想要的颜色。打开laza......