题目描述:
返回 A 的最短的非空连续子数组的长度,该子数组的和至少为 K 。
如果没有和至少为 K 的非空子数组,返回 -1 。
示例 1:
输入:A = [1], K = 1
输出:1
示例 2:
输入:A = [1,2], K = 4
输出:-1
示例 3:
输入:A = [2,-1,2], K = 3
输出:3
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shortest-subarray-with-sum-at-least-k
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
#include<iostream>
#include<stack>
#include<queue>
#include<deque>
using namespace std;
class Solution {
public:
int shortestS(vector<int> &nums, int K)
{
nums.insert(nums.begin(),0);
deque<int> dq;
//nums[0]=0;
// 前缀和
for(int i=1;i<nums.size();i++){
nums[i]=nums[i-1]+nums[i];
}
dq.push_front(0);
//minv=n+1
int minv=nums.size();
//cout<<minv<<endl;
for(int i=1;i<nums.size();i++){
/*保持栈中单调
*没有当前i的位置近而且比i的值大,因此右端点所需要的左端点肯定要将其舍弃
*前边的端点如果需要的,此时已经利用其更新了minv
*之所以不会将比i处值小的出栈,是因为还会因为他的值小会用到。
*过河拆桥,逐步添加,逐步舍弃大而远的元素
*/
while(!dq.empty()&&nums[i]<nums[dq.front()])
dq.pop_front();
while(!dq.empty()&&nums[i]-nums[dq.back()]>=K)
{
minv=min(minv,i-dq.back());
//cout<<minv<<endl;
dq.pop_back();
}
dq.push_front(i);
}
return minv==nums.size()?-1:minv;
}
};
int main()
{
vector<int> A;
int n,data,k;
cin>>n;
for(int i=0; i<n; i++)
{
cin>>data;
A.push_back(data);
}
cin>>k;
int result=Solution().shortestS(A,k);
cout<<result<<endl;
return 0;
}
技巧理解
1,前缀和
类似于动态规划不需要每次计算区间的和,很容易获得差值,在这不用将所有元素保留在队列中,只需要保留对应元素下标即可,为单调队列做准备。
2,单调队列
加入队列中的元素不单调,0 2 4下标对应元素分别为0 4 3,4的值比3大,而且下标远离于后续需要判断的元素,因此直接被淘汰了。如果前边用到了4这个元素,也已经更新minv,因此直接舍弃。
3,舍弃队列中元素
如果添加顺序下标为0 2 4(元素值单调),则需要从4 2 0依次判断是否大于当前元素,然后直接出队。如果从0 2 4 顺序判断,则没办法利用队列的特性出队。
4,选择k最小对应的元素
应该也从0 2 4的顺序判断,如果0处的值符合,依然可以将其舍弃,后续逐步判断队列中其他元素。0处值符合了,已经的到了一个minv,后续元素如果用到肯定比当前minv大,因此可以舍弃。
5,每次加入当前元素
当前元素的优势在于靠近后续元素,而且前边已经保证了单调。