0%

3098. 求出所有子序列的能量和

3098.求出所有子序列的能量和

题目描述:

给你一个长度为 n 的整数数组 nums 和一个 整数 k 。一个子序列的 能量 定义为子序列中 任意 两个元素的差值绝对值的 最小值

请你返回 nums 中长度 等于 k所有 子序列的 能量和

由于答案可能会很大,将答案对 $10^9 + 7$ 取余 后返回。

数据范围:

$2\le n \le 50, 2\le k \le n$

题解:

子序列,可以直接排序,然后就只需要考虑相邻两个元素的差值即可。

设计动态规划, $dp[i][k]$ 肯定需要,表示 $[0, i]$ 中以 $i$ 结尾的长度为 $k$ 的子序列的能量和。

另一个状态就不好想了。另一个状态为当前序列的能量值, $dp[i][k][v]$ 表示以 $i$ 结尾的长度为 $k$ ,并且能量值为 $v$ 的个数,那么答案就是 $dp[i][k][v] \times v$ 。

枚举子序列中的前一个数字 $j$ ,则 $dp[i][k][\min(v, diff)] = dp[i][k][\min(v, diff)] + dp[j][k - 1][v]$

枚举子序列中的前一个数字 $j$ ,并且遍历所有的 $v$ ,然后更新 $i$ 。

需要四层循环, $O(n^4)$ 。

但是需要注意怎么存 $v$ ,以及初始条件。有点类似上一道题目,都是需要存起来所有的值。

这里可以直接使用 $map$ 。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const static long long mod = 1e9 + 7;
const long long INF_LL = 0x3f3f3f3f3f3f3f3f;
const int INF = 0x3f3f3f3f;
int sumOfPowers(vector<int> &nums, int k)
{
// 长度等于 k 的字序列的能量和
sort(nums.begin(), nums.end());
// dp[i][k][val] * val
int n = nums.size();
vector<vector<unordered_map<int, int>>> dp(n, vector<unordered_map<int, int>>(k + 1)); // dp[i][j][k] -> map[k] -> v
// dp[i][k][v] += dp[j][k - 1][v]

for (int i = 0; i < n; ++i) {
dp[i][1][INF] = 1;
for (int kk = 1; kk <= k; ++kk) {
for (int j = 0; j < i; ++j) {
for(auto& [v, value]: dp[j][kk - 1]) {
dp[i][kk][min(v, nums[i] - nums[j])] = (dp[i][kk][min(v, nums[i] - nums[j])] + value) % mod;
}
}
}
}
int ans = 0;
for (int i = 0; i < n; ++i) {
for(auto& [v, value]: dp[i][k]) {
ans = (ans + 1ll * dp[i][k][v] * v % mod) % mod;
}
}
return ans;
}
};