题目描述

leetcode891. 子序列宽度之和

一个序列的 宽度 定义为该序列中最大元素和最小元素的差值。

给你一个整数数组 nums ,返回 nums 的所有非空 子序列宽度之和 。由于答案可能非常大,请返回对 109 + 7 取余 后的结果。

子序列 定义为从一个数组里删除一些(或者不删除)元素,但不改变剩下元素的顺序得到的数组。例如,[3,6,2,7] 就是数组 [0,3,1,6,2,2,7] 的一个子序列。

 

示例 1:

输入:nums = [2,1,3]
输出:6
解释:子序列为 [1], [2], [3], [2,1], [2,3], [1,3], [2,1,3] 。
相应的宽度是 0, 0, 0, 1, 1, 2, 2 。
宽度之和是 6 。

示例 2:

输入:nums = [2]
输出:0

 

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105

题解——贪心,直观解释

思路

我们知道,一个数组有2^n-1非空子序列,枚举显然是不可行的。
换一个角度思考这个问题,事实上,对于每一个子序列,元素的顺序不影响结果,因为我们只要知道它的最大值和最小值就可以了。所以我们想知道,对每一个元素来说,有几个子序列以它为最大值,又有几个子序列以它为最小值
考虑以某个元素为最小值的序列个数。如果是整个序列的最小值,显然,所有包含它的子序列都以它为最小值,数目为2^(n-1)。那次小值呢?就是包含它,但不包含最小值的序列个数,就是2^(n-2),这样,我们就看出规律来了:如果某个数是数组中第k小的元素,那么以它为最小值的子序列就有2^(n-k)个。其实也很好理解,我们从选与不选的角度考虑子序列个数,当确定以第k小的元素为最小值时,前面k-1个都不能选,第k个必须选,剩下可选可不选的还有n-k个,子序列自然就是2^(n-k)个了。
同理,以第k大元素为最大值的子序列也有2^(n-k)个。
接下来考虑如何用代码计算。直接写是不可能的,因为不可能算2^100000,所以我们维护一个系数,从1到2^(n-1),每次乘二,根据上述的描述,可以推知,以第k小元素最大值的序列数=以第k大元素最小值的序列数=2^k,(k从0开始),所以我们从小到大遍历,每次加上(2^k)*第k小值,减去(2^k)*第k大值
排序时间复杂度O(nlogn),计算时间复杂度O(n)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public:
int sumSubseqWidths(vector<int>& nums) {
sort(nums.begin(),nums.end());//排序
long k=1,ans=0,mod=1e9+7;//k为子序列个数,也就是系数
for(int i=0;i<nums.size();++i)
{
ans=(ans+nums[i]*k-nums[nums.size()-1-i]*k)%mod;
k=(k*2)%mod;
}
return ans;
}
};