2813. 子序列最大优雅度
问题描述
给你一个长度为 n
的二维整数数组 items
和一个整数 k
。
items[i] = [profiti, categoryi]
,其中 profiti
和 categoryi
分别表示第 i
个项目的利润和类别。
现定义 items
的 子序列 的 优雅度 可以用 total_profit + distinct_categories2
计算,其中 total_profit
是子序列中所有项目的利润总和,distinct_categories
是所选子序列所含的所有类别中不同类别的数量。
你的任务是从 items
所有长度为 k
的子序列中,找出 最大优雅度 。
用整数形式表示并返回 items
中所有长度恰好为 k
的子序列的最大优雅度。
**注意:**数组的子序列是经由原数组删除一些元素(可能不删除)而产生的新数组,且删除不改变其余元素相对顺序。
示例 1:
输入:items = [[3,2],[5,1],[10,1]], k = 2
输出:17
解释:
在这个例子中,我们需要选出长度为 2 的子序列。
其中一种方案是 items[0] = [3,2] 和 items[2] = [10,1] 。
子序列的总利润为 3 + 10 = 13 ,子序列包含 2 种不同类别 [2,1] 。
因此,优雅度为 13 + 22 = 17 ,可以证明 17 是可以获得的最大优雅度。
示例 2:
输入:items = [[3,1],[3,1],[2,2],[5,3]], k = 3
输出:19
解释:
在这个例子中,我们需要选出长度为 3 的子序列。
其中一种方案是 items[0] = [3,1] ,items[2] = [2,2] 和 items[3] = [5,3] 。
子序列的总利润为 3 + 2 + 5 = 10 ,子序列包含 3 种不同类别 [1, 2, 3] 。
因此,优雅度为 10 + 32 = 19 ,可以证明 19 是可以获得的最大优雅度。
示例 3:
输入:items = [[1,1],[2,1],[3,1]], k = 3
输出:7
解释:
在这个例子中,我们需要选出长度为 3 的子序列。
我们需要选中所有项目。
子序列的总利润为 1 + 2 + 3 = 6,子序列包含 1 种不同类别 [1] 。
因此,最大优雅度为 6 + 12 = 7 。
提示:
1 <= items.length == n <= 105
items[i].length == 2
items[i][0] == profiti
items[i][1] == categoryi
1 <= profiti <= 109
1 <= categoryi <= n
1 <= k <= n
解题思路与代码实现
按照利润从大到小排序。先把前 k 个项目选上。
考虑第 k+1个项目,如果要选它,我们必须从前 k 个项目中移除一个项目。
由于已经按照利润从大到小排序,选这个项目不会让 totalProfit
变大,所以重点考虑能否让 distinctCategories
变大。
class Solution {
public long findMaximumElegance(int[][] items, int k) {
// items数组按照profit降序排序
Arrays.sort(items, (item1, item2) -> item2[0] - item1[0]);
long res = 0; // 保存最终结果
long totalProfit = 0; // 记录当前的profit和
Set<Integer> categorySet = new HashSet<>(); // 记录出现过的category的集合
Stack<Integer> stack = new Stack<>(); // 记录出现过多次的category的profit
for (int i = 0; i < items.length; i++) {
int profit = items[i][0]; // 利润
int category = items[i][1]; // 类别
if (i < k) {
totalProfit += profit; // 前k个项目的profit直接累加
if (!categorySet.add(category)) { // 出现过的category记录到set集合中
// 重复类别,添加到栈中
stack.add(profit);
}
} else if (!stack.isEmpty() && categorySet.add(category)) { // set的add方法对于已存在的元素会返回false
// 如果当前类别已出现过,不需要考虑,它不会使distinct_categories变大,自然不会使优雅度变大
// 如果当前类别未出现过,考虑移除已出现过的哪个项目:
// 如果移除的项目的类别只出现一次(stack为空),不会改变distinct_categories,优雅度不会增大,无需考虑
// 如果移除的项目的类别出现多次(stack不为空),distinct_categories会增加1,可能会导致优雅度变大,这里选栈顶profit最小的进行淘汰
totalProfit += profit - stack.pop(); // 选一个重复类别的最小利润进行替换
}
// 更新当前最大优雅度
res = Math.max(res, totalProfit + (long) categorySet.size() * categorySet.size());
}
return res;
}
}
参考链接:灵茶山艾府 反悔贪心