題解/算法 {2945. 找到最大非递减数组的长度}
@LINK: https://leetcode.cn/problems/find-maximum-non-decreasing-array-length/
;
使用貪心(從左往右, 把A[i]
添加到A[i-1]
裡面) 是錯誤的, 比如對於[520,531,573,65,426,501,955]
正確的做法是:520, 531, (573+65), (426+501), 955
; 如果用貪心 你肯定會把426
放到64
裡面;
.
不要根據這個特殊樣例 而想著 在你原先錯誤的算法上 做一些特判/做一些優化代碼 然後試圖AC, 這是錯誤的! 這是在投機取巧! 即便你AC裡 還是會被Hack掉; 不要誤入歧途!
正確做法是DP, DP(i)
將A[...i]
變成遞增的最長長度, Last(i)
為DP(i)
所對應的序列的最後元素的最小值;
尋找j : [0...i)
, 如果Sum[j+1,...i] >= last[j]
則更新DP(j)+1 -> DP(i)
;
.
因為DP是單調遞增的, 所以 從j: [i-1,...,0]
遍歷, 一旦滿足條件 就break
;
int findMaximumLength(vector<int>& A) {
int N = A.size();
DP[0]=1, Last[0]=A[0];
FOR_( i, 1, N-1){
int64_t sum=A[i];
bool succ = false;
FOR_R_( p, i-1, 0){
if( sum >= Last[p]){
DP[i] = 1 + DP[p]; Last[i] = sum; succ=true;
break;
}
sum += A[p];
}
if( succ==false){
DP[i]=1; Last[i]=sum;
}
}
return DP[N-1];
}
注意 這個break
(即DP是單調的), 非常重要, 他為接下來的優化 提供支持;
由於他是N*N
超時的, 要優化 即優化DP;
我們要找(從右到左)第一個滿足Sum[j+1,...,i] >= Last[j]
, 因為j
是變量 所以左右式子都是變量, 我們化簡下, 令Sum[i] = A[0...i]前綴和
, 則Sum[i] >= Last[j] + Sum[j]
, 此時Sum[i]
是常數, 還有一個性質 Sum[i]
也是遞增的 (Last
不是單調的);
.
我們令wth[i] = Last[i] + Sum[i]
, 比如此時是wth = [ 2, 1, 5, 3, 7, 7, 6, 9, 6]
, 此時你要查詢Sum[i]
, 對於Sum[i] >=6
則會選擇6(第二個)
, 對於[3,5]
會選擇3
, 對於[1,2]
選擇1
, 對於<1
的 是無解;
.
我們從右往左 選擇嚴格遞減序列, 即Que= [1, 3, 6(第二個)]
, 維護這個序列, 對於當前的Sum[i]
比如他是4
, 那麼從前往後 把1
給扔掉 (因為以後的Sum[i]>=4
不會得到1
), 然後Que=[3,6]
此時3
就是答案; 然後再把當前的Last[i]+Sum[i]
放到Que.back()
; 這可以用deque
來維護;
int findMaximumLength(vector<int>& A) {
int N = A.size();
FOR_( i, 0, N-1){ Sum[i]=A[i]; if(i>0){ Sum[i]+=Sum[i-1];}}
deque< pair<int64_t, int> > Que; // {last[i]+Sum[i], i}
FOR_( i, 0, N-1){
auto & curDP = DP[i];
int64_t curLast;
if( i==0){
curDP = 1; curLast=A[0];
Que.push_back( {curLast + Sum[i], i});
continue;
}
//> 比如`Que=[1,2,3,4,5], Sum[i]=3`, 則變成`Que=[3,4,5]`;
while( Que.size()>=2 && Que.front().first<=Sum[i]){
auto fir = Que.front(); Que.pop_front();
if( Que.front().first > Sum[i]){
Que.push_front( fir);
break;
}
}
if( Que.empty() || Que.front().first>Sum[i]){
curDP = 1; curLast = Sum[i];
}
else{
curDP = 1 + DP[ Que.front().second]; curLast = Sum[i]-Sum[ Que.front().second];
}
int64_t wth = curLast + Sum[i];
while( Que.size()>0 && Que.back().first>=wth){ Que.pop_back();}
Que.push_back( {wth, i});
}
return DP[N-1];
}