动态规划||单调队列:LeetCode1425.带限制的子序列和

1425.带限制的子序列和

 

 意思就是寻找最大子区间和,子区间的每一个相邻元素可以下表最多相差k.

一、

想到求最大连续子区间和O(n)的方法,但是这题显然状态转移方程并不是dp[i]=max(dp[i-1],0)+a[i],他可以跳过最多k-1个元素 所以里面应该嵌套一层循环 for(int j=1->k)dp[i]=max(dp[i-j],dp[i]);循环后dp[i]=max(dp[i],0)+a[i];时间复杂度为O(n*k).但我们看数据范围:

 

 n和k都可以达到1e5,则时间复杂度最多达到1e10,显然超时,需要改进算法。

二、Binary tree

我们就想到用binary tree ,用multiset这个可以自动排序来存储每i个的前k个,有点想slide window,长度为k的窗口一直在滑动,然后每次dp[i]取multiset中的最大值,如果最大值都小于0就取0,加上a[i]即可。外层循环dp[i],时间复杂度为n,内层循环利用multiset的equal_range,时间复杂度为logk,所以总的时间复杂度为O(n*logk),不会超时。

上代码:

 1 //Binary tree  nlogk
 2 class Solution {
 3 public:
 4     int constrainedSubsetSum(vector<int>& nums, int k)
 5     {
 6         const int n = nums.size();
 7         multiset<int>m{-0x7fffffff};//必须得给这个初始化一个最小值 !!这样就不用检查multiset是不是空的
 8         vector<int>dp(n);
 9         int ans = -0x7fffffff;
10         for (int i = 0; i < n; ++i)
11         {
12             if (i > k)
13                 m.erase(m.equal_range(dp[i - (1 + k)]).first);
14             dp[i] = max(*rbegin(m), 0) + nums[i];//在这地方如果multiset是空的就会出错 加一个最小值在m中防止报错
15             m.insert(dp[i]);
16             ans = max(dp[i], ans);
17         }
18         return ans;
19     }
20 };

 

 

三、Monotonic queue

也可以利用单调队列,时间复杂度为O(n)

 1 class Solution {
 2 public:
 3     int constrainedSubsetSum(vector<int>& nums, int k)
 4     {
 5         const int n = nums.size();
 6         vector<int>dp(n);
 7         deque<int>q;//存的是index 下标
 8         int ans = -0x7fffffff;
 9         for (int i = 0; i < n; ++i)
10         {
11             if (i > k && q.front() == i - (k + 1))//可以选择坐标相差k 但相差k+1就不行了 得从窗口中出队
12                 q.pop_front();
13             dp[i] = (q.empty() ? 0 : max(dp[q.front()], 0)) + nums[i];
14             while (!q.empty() && dp[i] >= dp[q.back()])
15                 q.pop_back();
16             q.push_back(i);
17             ans = max(ans, dp[i]);
18            
19         }
20         return ans;
21     }
22 };

 

posted @ 2022-04-17 10:17  朱朱成  阅读(48)  评论(0编辑  收藏  举报