1. 题目来源
2. 题目说明

3. 题目解析
方法一:dp优化+巧妙解法
属实没搞懂怎么下手…看了大佬的讲解很久…dp 蒻鷄太难了…
最大子序列问题的变种,也是一道 dp 优化问题。为什么需要优化呢,先来看看暴力 dp 的思路吧。
dp[i]表示强制选择第i结尾的最大子序列的最大和值是多少dp[i]=nums[i-1]+max{dp[j]},i-k<=j<i时间复杂度 O ( n k ) O(nk) O(nk) 数据范围决定了,这个时间复杂度不理想状态下会达到 O ( n 2 ) O(n^2) O(n2),那么铁定TLE
所以需要进行优化。优化思路如下:
max{dp[j]}可以使用一个优先队列进行维护,队列元素为一个pair对,即pair<dp[i], i>,那么在优先队列内会按照dp[i]从大到小存储,那么转移的时候取优先队列队头就行了。但同时需要满足i-k<=j<i这个条件,将条件进行转化那么就有i - top.second > k成立,即不在范围内的top元素将其pop出队,因为这个dp[j]对于以后的状态转移不会对后面的dp[i]造成影响了,即直接弹出队头- 若弹出还非空,那么
dp[i]就从当前值和 ·top().first + 当前值两者取最
大值进行转移就可以了
这样做的目的就是使用优先队列成功将 max{dp[j]},i-k<=j<i 的时间复杂度降为
l
o
g
log
log 级的复杂度,那么整体复杂度即为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn) 是一个理想的复杂度情况了。
学的太渣了…dp 优化还没经历过,虽然感觉挺好理解的这种方法,但还是在本地进行了单步调试才理解了这个思想,确实强啊。近期也准备针对 dp 开始刷题了,希望能得到进步。
参见代码如下:
// 执行用时 :768 ms, 在所有 C++ 提交中击败了100.00%的用户
// 内存消耗 :157 MB, 在所有 C++ 提交中击败了100.00%的用户
#define PII pair<int, int>
#define MP(x, y) make_pair(x, y)
const int MAXN = 1e5 + 50;
priority_queue<PII> que;
int dp[MAXN];
class Solution {
public:
int constrainedSubsetSum(vector<int>& nums, int k) {
int n = nums.size();
while(!que.empty()) que.pop();
for (int i = 1; i <= n; i++){
int v = nums[i - 1];
dp[i] = v;
while(!que.empty() and i - que.top().second > k) que.pop();
if (!que.empty()) dp[i] = max(dp[i], que.top().first + v);
que.push(MP(dp[i], i));
}
int ans = dp[1];
for (int i = 2; i <= n; i++) ans = max(ans, dp[i]);
return ans;
}
};
本文深入解析了一道动态规划优化问题——带限制的子序列和(LeetCode 5180),通过引入优先队列来降低时间复杂度至O(nlogn),并分享了代码实现细节。
447

被折叠的 条评论
为什么被折叠?



