1. 程式人生 > 實用技巧 >Codeforces Round #669 (Div. 2) D. Discrete Centrifugal Jumps

Codeforces Round #669 (Div. 2) D. Discrete Centrifugal Jumps

D. Discrete Centrifugal Jumpsbu6

題意:

給你1到n的高樓, 現在你在1的位置你可每次從 i 到 i + 1, 或者跳到 i < j 且 \(max(h[i], h[j]) < min(h[i + 1] ...,h[j - 1])\)

或者跳 i < j 且 \(min(h[i], h[j]) > max(h[i + 1], ......, h[j - 1])\)

問最少需要跳多少下可以從1到n

題解:

這題不難想到dp

比賽的時候貪心, 每個位置能跳的最遠位置, 然後dp, 後來被hack了

比如這個樣例

8
20 3 4 19 20 21 20 19
如果貪心每個位置跳的最遠位置,那麼第一個可以跳到5然後第五個在跳到第7個, 第7個跳到第8個, 總共跳了3次
但是 答案是2次, 第一個位置可以跳到 第四個位置, 然後第四個位置跳到最後一個位置。

如果這題可以暴力那麼就直接把每個位置能走的點都走了, 然後選一個最小的。

時間肯定不行。

如果加上記憶化暴力了, 那就是 o(\(n ^ 2\)) 也就是\(n ^ 2\)的dp

n是3e5 還不行。

如果加上 單調棧維護dp就可以把複雜度降到 o(n)了

用兩個單調棧, q, q1, q維護嚴格單調遞減, 當出棧是表示 q.top()的元素可以得到i

也就是 把i所有能去的地方都列舉到了

同理q1維護 嚴格單調遞增

程式碼:

#include<bits/stdc++.h>
using namespace std;

const int N = 3e5 + 7;

int n, a[N], dp[N];

stack<int> q, q1;

int main() {
    ios::sync_with_stdio(0);
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        dp[i] = n + 1;
    }
    dp[1] = 0;
    for (int i = 1; i <= n; i++) {
        dp[i] = min(dp[i - 1] + 1, dp[i]);
        while (!q.empty() && a[q.top()] <= a[i]) {
            int x = a[q.top()];
            q.pop();
            if (a[i] == x) continue;
            if (!q.empty()) {
                dp[i] = min(dp[i], dp[q.top()] + 1);
            }
        }

        while (!q1.empty() && a[q1.top()] >= a[i]) {
            int x = a[q1.top()];
            q1.pop();
            if (x == a[i]) continue;
            if (!q1.empty()) {
                dp[i] = min(dp[i], dp[q1.top()] + 1);
            }
        }
        q.push(i);
        q1.push(i);
        
    }
    cout << dp[n]  << endl;


}