算法记录

81 阅读2分钟

反悔贪心法

2813. 子序列最大优雅度

给你一个长度为 n 的二维整数数组 items 和一个整数 k 。

items[i] = [profiti, categoryi],其中 profiti 和 categoryi 分别表示第 i 个项目的利润和类别。

现定义 items 的 子序列 的 优雅度 可以用 total_profit + distinct_categories2 计算,其中 total_profit 是子序列中所有项目的利润总和,distinct_categories 是所选子序列所含的所有类别中不同类别的数量。

你的任务是从 items 所有长度为 k 的子序列中,找出 最大优雅度 。

用整数形式表示并返回 items 中所有长度恰好为 k 的子序列的最大优雅度。

注意:数组的子序列是经由原数组删除一些元素(可能不删除)而产生的新数组,且删除不改变其余元素相对顺序。

  • 将项目按照利润从大到小排序,先选上前k个项目,并维护一个 set 和一个 array,一个 totalProfit
    • categorySet 记录现有的类别
    • 如果现有类别已有,当前遍历到的项目放入 dups 数组中
    • totalProfit 当前利润总和
  • 计算当前的优雅度 ans
  • 继续遍历剩下的项目(并且 dups 有内容)
    • 如果此项目的类别已存在,跳过,因为他的利润一定是更小的
    • 如果此项目的类别不存在,放入 categorySet 中,并从 dups 里面拿最后一个,进行替换操作
      • 替换是指把 totalProfit 中原有的利润减去,换成当前项目的利润,然后重新计算优雅度
      • 如果比 ans 大,ans 取此值

关键点在于,替换操作可能导致优雅度变小,但是我们一定要做这个操作,因为类别的平方,后期可能越来越大,我们要贪心的容纳尽可能多的类别。

function findMaximumElegance(items: number[][], k: number): number {
  items.sort((a, b) => b[0] - a[0])

  let total_profit = 0

  let caMap = new Set()
  let dups: number[][] = []
  for (let i = 0; i < k; i++) {
    total_profit += items[i][0]
    const category = items[i][1]
    if (!caMap.has(category)) {
      caMap.add(category)
    } else {
      dups.push(items[i])
    }
  }
  let ans = total_profit + caMap.size * caMap.size

  const n = items.length
  for (let i = k; i < n && dups.length; i++) {
    const category = items[i][1]
    if (caMap.has(category)) {
      continue
    }
    // 挑一个替换
    const old = dups.pop() || [0, 0]
    total_profit -= old[0]
    total_profit += items[i][0]
    caMap.add(category)
    ans = Math.max(ans, total_profit + caMap.size * caMap.size)
  }

  return ans
};