2026-01-14:排序排列。用go语言,有两个等长的整数数组 value 和 limit,表示 n 个元素的得分和操作限制。初始时所有元素都处于未激活状态,

8 阅读5分钟

2026-01-14:排序排列。用go语言,有两个等长的整数数组 value 和 limit,表示 n 个元素的得分和操作限制。初始时所有元素都处于未激活状态,你可以按任意顺序逐个激活它们,但激活第 i 个元素时,当前处于“活跃”状态的元素数量必须严格小于 limit[i]。

每次激活第 i 个元素时,就把 value[i] 加入累计得分(即统计所有曾被激活过的元素的 value 之和)。

此外,每次激活操作结束后,若此刻活跃元素的数量为 x,那么所有满足 limit[j] <= x 的元素 j 都会被立即并永久设为非活跃(包括那些刚才可能处于活跃状态的元素),并且这些被置为非活跃的元素以后不能再次激活。请计算通过选择最优的激活顺序能够得到的最大累计得分。

1 <= n == value.length == limit.length <= 100000。

1 <= value[i] <= 100000。

1 <= limit[i] <= n。

输入: value = [3,5,8], limit = [2,1,3]。

输出: 16。

解释:

一个最优的激活顺序是:

步骤激活的 ivalue[i]激活 i 前的活跃数激活 i 后的活跃数变为非活跃的 j非活跃元素总和
11501j = 1(因为 limit[1] = 1)[1]5
20301[1]8
32812j = 0(因为 limit[0] = 2)[0, 1]16

因此,可能的最大总和是 16。

题目来自力扣3645。

算法步骤说明

该算法的核心思路是按限制条件(limit)分组处理,并在每组内部优先选择价值(value)高的元素。具体步骤如下:

  1. 分组 首先,算法根据每个元素的 limit 值进行分组。limit 值相同的元素会被分到同一组。例如,在示例中,limit 数组为 [2, 1, 3],那么 limit 为 1、2、3 的元素会分别被归入不同的组。groups[lim] 这个切片就存储了所有 limit 值为 lim 的元素的 value

  2. 组内排序与选择 接着,算法遍历每一个分组。对于某个分组,其键为 lim(即限制条件),值为该组内所有元素的 value 列表 a

    • 关键判断:如果该组的限制条件 lim 小于组内元素的数量 len(a),这意味着我们不能激活组内的所有元素,否则在激活最后一个元素时,活跃元素数量将等于 lim,导致该组(甚至其他组)的元素被全部置为非活跃。因此,我们必须做出选择。
    • 贪心选择:为了最大化总得分,最优策略是只保留该组中价值最大的 lim 个元素。这是典型的贪心算法思想,即在局部做出最优决策(选择价值最高的),从而期望获得全局最优解。实现上,先将组内元素按价值从小到大排序,然后只取排序后末尾的 lim 个元素。
  3. 累计得分 在处理完每个组(即完成组内筛选后),算法会将该组最终保留的所有元素的价值累加到总得分 ans 中。因为每个元素只能被激活一次,且被激活后其价值就被永久计入总分,所以这里直接求和即可。

时间复杂度与空间复杂度分析

  • 总的时间复杂度

    1. 分组:遍历 limit 数组并将 value 放入对应的组中,时间复杂度为 O(n)。
    2. 组内处理:这是主要开销所在。最坏情况下,所有元素都具有相同的 limit 值,那么只需要对一个包含 n 个元素的组进行排序。使用 Go 标准库的快速排序平均时间复杂度为 O(n log n)。其他组的处理时间可以忽略。因此,总的时间复杂度为 O(n log n)
  • 总的额外空间复杂度

    1. 存储分组:需要额外的空间来存储分组 groups,其大小取决于 limit 的取值范围。由于 limit[i] <= n,所以 groups 切片的最大长度为 n+1。最坏情况下,每个元素都属于不同的组,因此总共需要存储 n 个元素的引用,空间复杂度为 O(n)。

综上所述,该算法通过巧妙的分组和贪心选择策略,高效地解决了在特定激活规则下最大化总得分的问题。其总的时间复杂度为 O(n log n),总的额外空间复杂度为 O(n)

Go完整代码如下:

package main

import (
	"fmt"
	"slices"
)

func maxTotal(value, limit []int) (ans int64) {
	groups := make([][]int, len(value)+1)
	for i, lim := range limit {
		groups[lim] = append(groups[lim], value[i])
	}

	for lim, a := range groups {
		if lim < len(a) {
			// 只取最大的 lim 个数
			slices.Sort(a)
			a = a[len(a)-lim:]
		}
		for _, x := range a {
			ans += int64(x)
		}
	}
	return
}

func main() {
	value := []int{3, 5, 8}
	limit := []int{2, 1, 3}
	result := maxTotal(value, limit)
	fmt.Println(result)
}

在这里插入图片描述

Python完整代码如下:

# -*-coding:utf-8-*-

from typing import List

def max_total(value: List[int], limit: List[int]) -> int:
    ans = 0
    n = len(value)
    groups = [[] for _ in range(n + 1)]
    
    # 按照 limit 分组
    for i in range(n):
        groups[limit[i]].append(value[i])
    
    # 处理每个分组
    for lim, a in enumerate(groups):
        if not a:
            continue
        if lim < len(a):
            # 只取最大的 lim 个数
            a.sort()
            a = a[-lim:]
        ans += sum(a)
    
    return ans

def main():
    value = [3, 5, 8]
    limit = [2, 1, 3]
    result = max_total(value, limit)
    print(result)

if __name__ == "__main__":
    main()

在这里插入图片描述

C++完整代码如下:

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>

using namespace std;

long long maxTotal(vector<int>& value, vector<int>& limit) {
    int n = value.size();
    vector<vector<int>> groups(n + 1);

    for (int i = 0; i < n; i++) {
        groups[limit[i]].push_back(value[i]);
    }

    long long ans = 0;

    for (int lim = 0; lim < groups.size(); lim++) {
        auto& a = groups[lim];
        if (a.empty()) continue;

        if (lim < a.size()) {
            sort(a.begin(), a.end(), greater<int>());  // 降序排序
            ans += accumulate(a.begin(), a.begin() + lim, 0LL);
        } else {
            ans += accumulate(a.begin(), a.end(), 0LL);
        }
    }

    return ans;
}

int main() {
    vector<int> value = {3, 5, 8};
    vector<int> limit = {2, 1, 3};

    long long result = maxTotal(value, limit);
    cout << result << endl;

    return 0;
}

在这里插入图片描述