題解/算法 {2945. 找到最大非递减数组的长度}

題解/算法 {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];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值