給定一個數組,值可以爲正、負和0,請返回累加和小於等於k的最長子數組長度。
思路
申請兩個輔助數組,分別爲 min_v[N] 和 map_idx[N]
min_v[i] 用於記錄從i開始,往右累加的最小和
map_idx[i]由於記錄,得到最小和的右邊界的下標
min_v[i]的記錄規則就是,從優往左進行遍歷,如果累加和一直<0 那麼就一直累加下去,這個條件下,map_idx[i]就一直記錄之前的右邊界。
如果發現相加之後大於零,那麼就另起爐竈,從自己開始作爲右邊界的開始,以及往右累加的最小和。
在計算最長長度時,通過計算每個min_v[i],可以進行整塊的區間的相加,對應的下標也就可以通過map_idx[i]這個右邊界進行跳躍。直到出現 sum >K 或者遍歷結束。
以上面這種方式,我們從 第1,2,3,4 個位置分別開始計算,由於sum是累加和,
當從第2個開始時,直接去掉第一個的值就行,即 sum-=arr[1],然後再計算,看看之前sum >K的情況是否有所改變。
代碼
#include<iostream>
#include<ctime>
#include<cstdlib>
#include<map>
using namespace std;
int N;
int arr[1000]={0};
void rand_arr(int N){
srand((int)time(0));
for(int i=0;i <N;i++){
arr[i] = rand()%11 -2; //[-2,5]
}
// for(int i=0;i <N;i++){
// arr[i] = 5;
// }
// arr[N-1] = 2;
}
void printarr(int N){
for(int i=0;i <N;i++){
printf("%d ",arr[i]);
}
printf("\n");
}
void init(int N){
rand_arr(N);
printarr(N);
}
//保存當前下標範圍內的最小和
int min_v[1000]={0};
//記錄當前下標和右邊界
map<int,int> map_idx;
int main() {
N = 10;
init(N);
int K = 2;
map_idx[N-1] =N-1;
min_v[N-1] = arr[N-1];
//從後往前累加
for(int i=N-2;i>=0;i--){
if(min_v[i+1]<0){
min_v[i] = min_v[i+1] + arr[i];
map_idx[i] = map_idx[i+1];
}else{
min_v[i] = arr[i];
map_idx[i] = i;
}
}
//記錄長度
int len =0;
//記錄目前右邊界
int end=0;
//記錄目前的sum值
int sum=0;
//因爲右邊界只會往右邊挪,並且sum可以一直累加
for(int i=0;i<N;i++){
while(sum+min_v[end]<=K && end<N){
sum+=min_v[end];
end = map_idx[end]+1;
}
//記錄最大len
len = max(len,end-i);
//如果 end 和 i 相等的話,說明沒有進入循環,即 sum根本沒有加
sum -= end>i? arr[i]:0;
//保持 end >=i;
end = max(end,i+1);
}
printf("%d\n",len);
return 0;
}