leetcode(力扣) 2866. 美麗塔 II

小程xy發表於2024-04-28

原題連結

暴力做法 (時間複雜度 O(n^2))

每次選取下標 i 為峰值, 進行 n 次,對每次取max就可以找到答案


對於 i 左邊的序列: 需要滿足序列是非遞減的, 同時每個值儘可能大

所以滿足: j 的位置上的數 <= (j, i] 上的最小的值 (等於時取得最大值) , 同時需要保證 j 位置上的數要小於heights[j] (題目中的要求,美麗塔的要求); 即 t = min(pre, heights[j]) pre表示的是 下標是 (j, i] 的最小的值


對於 i 右邊的序列: 需要滿足序列是非遞增的,同時每個值儘可能大

所以滿足:j 的位置上的數 <= [i, j) 上的最小的值 (等於時取得最大值), 同時需要保證 j 位置上的數要小於heights[j]; 即 t = min(pre, heights[j]) pre表示的是 下標是 [i, j) 的最小的值

class Solution {
public:
    long long maximumSumOfHeights(vector<int>& heights) {
        int n = heights.size();
        long long res = 0;
        for (int i = 0; i < n; i ++ )
        {
            int pre = heights[i];
            long long sum = heights[i] * 1LL;
            for (int j = i - 1; j >= 0; j --)  // pre表示的是下標 (j, i] 中heights中的最小值
            {    
                sum += min(pre, heights[j]);    // 下標從i - 1開始, 每次取min,可以保證當下標為 j 時 pre 表示的就是 [j + 1, i] 的最小值
                pre = min(pre, heights[j]);    
            }
            pre = heights[i];
            for (int j = i + 1; j < n; j ++)  // pre表示的是下標 [i, j) 中heights中的最小值
            {
                sum += min(pre, heights[j]);
                pre = min(pre, heights[j]);
            }
            res = max(sum, res);
        }
        return res;
    }
};

單調棧做法 (時間複雜度 O(n)) (tag: 單調棧、 動態規劃)

st1 和 st2 存的都是下標

下面圖片模擬的是 第一個 for迴圈, 標紅的是 進入 if(st1.empty()) 這個分支的
p1[4] 為什麼加的是 2 x heights[4]呢, 因為此時st1中還有 元素1的下標2, 此時 下標3 和 下標4 的高度應該都為heights[4]

class Solution {
public:
    long long maximumSumOfHeights(vector<int>& heights) {
        int n = heights.size();
        
        long long res = 0;
        stack<int> st1; stack<int> st2;  // 棧裡面存的是 下標
        vector<long long> p1(n); vector<long long> p2(n);    
        // p1 的狀態表示是 下標為 i 的 左側美麗塔高度之和 (包含i本身,同時滿足p1[i]是美麗塔高度和的最大值)
        // p2 的狀態表示是 下標為 i 的 右側美麗塔高度之和 (包含i本身,同時滿足p1[i]是美麗塔高度和的最大值)

        for (int i = 0; i < n; i ++)
        {
            while (!st1.empty() && heights[i] < heights[st1.top()]) st1.pop(); // 讓棧滿足 (棧為空 || 棧中元素對應的heights的值是非遞減的)
            
           // 棧為空 說明 : i = 0或者 i 前面的山脈高度都比 heights[i] 大, 為了保證序列非遞減, 前面的所有數都應該是 heights[i]
            if (st1.empty()) p1[i] = (long long)(i + 1) * heights[i]; 
            else p1[i] = p1[st1.top()]  + (long long)(i - st1.top()) * heights[i] ;
          // 不為空 說明 下標為 (st1.top(), i] 山脈高度 都比 heights[i] 大, 為了保證序列非遞減,  (st1.top(), i] 之間的山脈高度都應該是  heights[i]

            st1.emplace(i);
        }

        for (int i = n - 1; i >= 0; i --)  // 需要保證從後往前是一個非遞減序列
        {
            while (!st2.empty() && heights[i] < heights[st2.top()]) st2.pop(); 

            if (st2.empty()) p2[i] = (long long)(n - i) * heights[i] * 1LL;
            else p2[i] = p2[st2.top()] + (long long)(st2.top() - i) * heights[i] ;
            st2.emplace(i);

            res = max(res, p1[i] + p2[i] - heights[i]);  // 看p1和p2的狀態表示,都包含的 i,要減去重複的一個
        }
        return res;
    }
};

覺得寫的不錯的話,點個贊吧!~

相關文章