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;


}
posted @ 2020-09-09 12:54  ccsu_zhaobo  阅读(201)  评论(0编辑  收藏  举报