题目描述:
给你一个整数数组 nums
和一个整数 k
,找出三个长度为 k
、互不重叠、且全部数字和(3 * k
项)最大的子数组,并返回这三个子数组。
以下标的数组形式返回结果,数组中的每一项分别指示每个子数组的起始位置(下标从 0 开始)。如果有多个结果,返回字典序最小的一个。
数据范围:
$1\le nums.len \le 2\times 10^4; 1\le nums[i] \lt 2^{16}; 1\le k \le \lfloor \frac{nums.len}{3} \rfloor$
题解:
如果使用 $dp[i]$ 表示前 $i$ 个里面权重最大的子数组,需要枚举选不选 $nums[i]$,而且最终的子数组个数还有限制。需要把子数组个数也作为状态,即 $dp[i][j]$ 表示已经有 $i$ 个子数组,并且访问到了 $nums[j]$。则枚举选与不选 $nums[j]$,选的话需要以 $nums[j]$ 为结尾,前面 $k$ 个都要选;否则的话就是 $j-1$。可以得到递推方程:
选的话就需要从 $dp[i-1]$ 转移过来。
而且需要记录方案,一般来说需要使用一个维数与 $dp$ 数组一样的 $pre$ 数组记录,而且 $pre$ 里面每个元素的维数需要和 $dp$ 的维数保持一致,也就是需要一个二维的 $pair$ 数组表示 $pre$。使用该种方法可以解决所有的路径记录问题。
如果 $i,j$ 可以很方便的找到前一个,就不需要用 $pre$ 记录。只需要判断 $dp[i][j]$ 和前一项的大小关系就行。但是像LIS之类的问题,不能知道 $dp[i]$ 是从哪个 $dp[j]$ 转移过来的,就需要使用辅助数组记录。
1 2 3 4 5 6 7 8 9 10 11 12 13
| int i = 3, j = n; vector<int> ans; while (i > 0 && j > 0) { if (pre[i][j].first == i - 1 && pre[i][j].second == j - k) { ans.emplace_back(j - k); } int tmpi = pre[i][j].first; int tmpj = pre[i][j].second; i = tmpi, j = tmpj; } reverse(ans.begin(), ans.end());
|
也可以使用下面这种方法记录,但是通用性比较弱。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int i = 3, j = n; vector<int> ans; while (j > 0) { if (dp[i][j] == dp[i][j - 1]) { j--; } else { ans.emplace_back(j - k); i--; j -= k; } }
|
代码:
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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| 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 int INF = 0x3f3f3f3f; vector<vector<int>> dp; vector<int> preSum; vector<int> maxSumOfThreeSubarrays(vector<int> &nums, int k) { int n = nums.size(); dp.resize(4, vector<int>(n + 1)); preSum.resize(n + 1);
int sum = 0; vector<vector<pair<int, int>>> pre(4, vector<pair<int, int>>(n + 1)); for (int i = 1; i <= n; ++i) { sum += nums[i - 1]; preSum[i] = sum; } for (int i = 1; i <= 3; ++i) { for (int j = 1; j <= n; ++j) { dp[i][j] = dp[i][j - 1]; pre[i][j] = {i, j - 1}; if (j - k >= 0) { int tmp = dp[i - 1][j - k] + preSum[j] - preSum[j - k]; if (tmp > dp[i][j]) { pre[i][j] = {i - 1, j - k}; dp[i][j] = tmp; } } } } cout << dp[3][n] << endl; int i = 3, j = n; vector<int> ans; while (i > 0 && j > 0) { if (pre[i][j].first == i - 1 && pre[i][j].second == j - k) { ans.emplace_back(j - k); } int tmpi = pre[i][j].first; int tmpj = pre[i][j].second; i = tmpi, j = tmpj; }
reverse(ans.begin(), ans.end()); return ans; } };
|