加载中...
1589-所有排列中的最大和(Maximum Sum Obtained of Any Permutation)
发表于:2021-12-03 | 分类: 中等
字数统计: 1.6k | 阅读时长: 8分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/maximum-sum-obtained-of-any-permutation

英文原文

We have an array of integers, nums, and an array of requests where requests[i] = [starti, endi]. The ith request asks for the sum of nums[starti] + nums[starti + 1] + ... + nums[endi - 1] + nums[endi]. Both starti and endi are 0-indexed.

Return the maximum total sum of all requests among all permutations of nums.

Since the answer may be too large, return it modulo 109 + 7.

 

Example 1:

Input: nums = [1,2,3,4,5], requests = [[1,3],[0,1]]
Output: 19
Explanation: One permutation of nums is [2,1,3,4,5] with the following result: 
requests[0] -> nums[1] + nums[2] + nums[3] = 1 + 3 + 4 = 8
requests[1] -> nums[0] + nums[1] = 2 + 1 = 3
Total sum: 8 + 3 = 11.
A permutation with a higher total sum is [3,5,4,2,1] with the following result:
requests[0] -> nums[1] + nums[2] + nums[3] = 5 + 4 + 2 = 11
requests[1] -> nums[0] + nums[1] = 3 + 5  = 8
Total sum: 11 + 8 = 19, which is the best that you can do.

Example 2:

Input: nums = [1,2,3,4,5,6], requests = [[0,1]]
Output: 11
Explanation: A permutation with the max total sum is [6,5,4,3,2,1] with request sums [11].

Example 3:

Input: nums = [1,2,3,4,5,10], requests = [[0,2],[1,3],[1,1]]
Output: 47
Explanation: A permutation with the max total sum is [4,10,5,3,2,1] with request sums [19,18,10].

 

Constraints:

  • n == nums.length
  • 1 <= n <= 105
  • 0 <= nums[i] <= 105
  • 1 <= requests.length <= 105
  • requests[i].length == 2
  • 0 <= starti <= endi < n

中文题目

有一个整数数组 nums ,和一个查询数组 requests ,其中 requests[i] = [starti, endi] 。第 i 个查询求 nums[starti] + nums[starti + 1] + ... + nums[endi - 1] + nums[endi] 的结果 ,starti 和 endi 数组索引都是 从 0 开始 的。

你可以任意排列 nums 中的数字,请你返回所有查询结果之和的最大值。

由于答案可能会很大,请你将它对 109 + 7 取余 后返回。

 

示例 1:

输入:nums = [1,2,3,4,5], requests = [[1,3],[0,1]]
输出:19
解释:一个可行的 nums 排列为 [2,1,3,4,5],并有如下结果:
requests[0] -> nums[1] + nums[2] + nums[3] = 1 + 3 + 4 = 8
requests[1] -> nums[0] + nums[1] = 2 + 1 = 3
总和为:8 + 3 = 11。
一个总和更大的排列为 [3,5,4,2,1],并有如下结果:
requests[0] -> nums[1] + nums[2] + nums[3] = 5 + 4 + 2 = 11
requests[1] -> nums[0] + nums[1] = 3 + 5  = 8
总和为: 11 + 8 = 19,这个方案是所有排列中查询之和最大的结果。

示例 2:

输入:nums = [1,2,3,4,5,6], requests = [[0,1]]
输出:11
解释:一个总和最大的排列为 [6,5,4,3,2,1] ,查询和为 [11]。

示例 3:

输入:nums = [1,2,3,4,5,10], requests = [[0,2],[1,3],[1,1]]
输出:47
解释:一个和最大的排列为 [4,10,5,3,2,1] ,查询结果分别为 [19,18,10]。

 

提示:

  • n == nums.length
  • 1 <= n <= 105
  • 0 <= nums[i] <= 105
  • 1 <= requests.length <= 105
  • requests[i].length == 2
  • 0 <= starti <= endi < n

通过代码

高赞题解

解决问题的思路非常简单:我们需要统计每个索引位置的查询次数。然后贪心:越大的数字,分配给查询次数越高的频次。

下面的问题就变成了:如何快速地统计每一个索引的查询次数。

最优方案,也是这个问题实现最简单的方案,就是使用扫描线。即对于每一个request[start, end],我们知道从 start 开始的数字多了一次查询,从 end + 1 开始的数字少了一次查询。

用一个 freq 数组,对于每一个 request[start, end],都进行 freq[start] ++freq[end + 1] -- 操作。

之后,freq[0...i] 的数字和,就是 i 这个索引的查询次数。

我的参考代码(C++):

class Solution {

private:
    const long long MOD = 1e9 + 7;

public:
    int maxSumRangeQuery(vector<int>& nums, vector<vector<int>>& requests) {

        int n = nums.size();

        vector<int> freq(n + 1);
        for(const vector<int>& v: requests)
            freq[v[0]] ++, freq[v[1] + 1] --;

        for(int i = 1; i <= n; i ++)
            freq[i] += freq[i - 1];

        sort(freq.begin(), freq.begin() + n);
        sort(nums.begin(), nums.end());

        long long res = 0;
        for(int i = n - 1; i >= 0; i --)
            res = (res + (long long)nums[i] * freq[i]) % MOD;
        return res;
    }
};

但是,这个问题,因为相当于每个 request,就是在一个区间范围内做 +1 操作,我的第一反应就是线段树。

因为需要区间更新,所以需要懒更新。竞赛选手直接使用模板就好了 >_<

如果有了线段树的代码,主逻辑很直接,如下:

class Solution {

private:
    const long long MOD = 1e9 + 7;

public:
    int maxSumRangeQuery(vector<int>& nums, vector<vector<int>>& requests) {

        int n = nums.size();
        SegmentTree tree(n);
        for(const vector<int>& v: requests)
            tree.add(v[0], v[1]);

        vector<int> freq(n);
        for(int i = 0; i < n; i ++) freq[i] = tree.query(i);

        sort(freq.begin(), freq.end());
        sort(nums.begin(), nums.end());

        long long res = 0;
        for(int i = n - 1; i >= 0; i --)
            res = (res + (long long)nums[i] * freq[i]) % MOD;
        return res;
    }
};

针对这个问题,我的包含懒更新的线段树代码如下,有需要的同学可以参考呀:

(不是通用模板,我针对这个问题本身进行了优化,关键是学习懒更新的思路)

class SegmentTree{

private:
    int n;
    vector<int> tree, lazy;

public:
    SegmentTree(int n): n(n), tree(4 * n, 0), lazy(4 * n, 0){}

    void add(int uL, int uR){
        update(0, 0, n-1, uL, uR);
    }

    int query(int index){
        return query(0, 0, n-1, index);
    }

private:
    void update(int treeID, int treeL, int treeR, int uL, int uR){

        if(lazy[treeID]){
            tree[treeID] += (treeR - treeL + 1) * lazy[treeID];
            if(treeL != treeR){
                lazy[2 * treeID + 1] += lazy[treeID];
                lazy[2 * treeID + 2] += lazy[treeID];
            }
            lazy[treeID] = 0;
        }

        if (treeL > uR || treeR < uL) return;

        if(uL <= treeL && uR >= treeR){
            tree[treeID] += treeR - treeL + 1;
            if(treeL != treeR){
                lazy[2 * treeID + 1] ++;
                lazy[2 * treeID + 2] ++;
            }
            return;
        }

        int mid = (treeL + treeR) / 2;
        update(2 * treeID + 1, treeL, mid, uL, uR);
        update(2 * treeID + 2, mid + 1, treeR, uL, uR);
        tree[treeID] = tree[treeID * 2 + 1] + tree[treeID * 2 + 2];
        return;
    }

    int query(int treeID, int treeL, int treeR, int index){

        if(lazy[treeID]){
            tree[treeID] += (treeR - treeL + 1) * lazy[treeID];
            if(treeL != treeR){
                lazy[2 * treeID + 1] += lazy[treeID];
                lazy[2 * treeID + 2] += lazy[treeID];
            }
            lazy[treeID] = 0;
        }

        if(treeL== treeR) return tree[treeID];

        int mid = (treeL + treeR) / 2;
        if(index <= mid) return query(2 * treeID + 1, treeL, mid, index);
        return query(2 * treeID + 2, mid + 1, treeR, index);
    }
};

关于线段树的懒更新,这篇文章可以做入门:https://leetcode.com/articles/a-recursive-approach-to-segment-trees-range-sum-queries-lazy-propagation/


统计信息

通过次数 提交次数 AC比率
4048 13713 29.5%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言
上一篇:
1588-所有奇数长度子数组的和(Sum of All Odd Length Subarrays)
下一篇:
1591-奇怪的打印机 II(Strange Printer II)
本文目录
本文目录