原题链接:3098. 求出所有子序列的能量和
题目描述:
给你一个长度为 n
的整数数组 nums
和一个 正 整数 k
。
一个子序列的 能量 定义为子序列中 任意 两个元素的差值绝对值的 最小值 。
请你返回 nums
中长度 等于 k
的 所有 子序列的 能量和 。
由于答案可能会很大,将答案对 10^9 + 7
取余 后返回。
输入输出描述:
示例 1:
输入:nums = [1,2,3,4], k = 3
输出:4
解释:
nums
中总共有 4 个长度为 3 的子序列:[1,2,3]
,[1,3,4]
,[1,2,4]
和 [2,3,4]
。能量和为 |2 - 3| + |3 - 4| + |2 - 1| + |3 - 4| = 4
。
示例 2:
输入:nums = [2,2], k = 2
输出:0
解释:
nums
中唯一一个长度为 2 的子序列是 [2,2]
。能量和为 |2 - 2| = 0
。
示例 3:
输入:nums = [4,3,-1], k = 2
输出:10
解释:
nums
总共有 3 个长度为 2 的子序列:[4,3]
,[4,-1]
和 [3,-1]
。能量和为 |4 - 3| + |4 - (-1)| + |3 - (-1)| = 10
。
提示:
2 <= n == nums.length <= 50
-10^8 <= nums[i] <= 10^8
2 <= k <= n
解题思路:
首先将所有元素从小到大排序,任意元素之间的最小差值等价于所有相邻元素差值的最小值,然后我们需要从n个元素中选出刚好k个元素,求所有这样的子序列的能量值的和,对于所有的长为k的子序列我们可以采取记忆化搜索的方式来处理,mem[i][j][v]表示从第i个元素开始并且选择了j个元素并且选择的所有元素中相邻元素的最小差值为v的所有子序列的能量值之和,知道了状态定义然后直接记忆化搜索即可。
时间复杂度:O(n^4*k),这个时间复杂度看着很大,好像有3e8那么大了,但是实际上是远远到不了3e8,因为我们枚举当前位置l,然后后一个位置i为从l+1这个位置开始枚举,同时i+(k-j)<=n这个条件也需要满足,那么枚举当前位置l和后一个位置i实际上时间大概是n*(n-1)/2,再加上后面的i+(k-j)<=n这个条件,就会更小,也就是说虽然表面是3e8这个量级,但是实际上还是会除以一个非常大的常数,所以说是远远达不到3e8这个量级的。
空间复杂度:O(n^3*k)。
cpp代码如下:
const int mod=1e9+7;
class Solution {
int n,k;
unordered_map<int,int>mem[50][51];
vector<int>a;
int dp(int l,int j,int v)
{
if(mem[l][j].count(v))return mem[l][j][v];
if(j==k)return v;
int res=0;
for(int i=l+1;i+(k-j)<=n;i++)
{
res+=dp(i,j+1,min(v,a[i]-a[l]));
res%=mod;
}
return mem[l][j][v]=res;
}
public:
int sumOfPowers(vector<int>& nums, int m) {
n=nums.size();
sort(nums.begin(),nums.end());
k=m;
a=nums;
int res=0;
for(int i=0;i+m<=n;i++) //枚举第一个位置选谁
{
res+=dp(i,1,int(1e9));
res%=mod;
}
return res;
}
};