Codeforces Round #669 (Div. 2) D. Discrete Centrifugal Jumps
阿新 • • 發佈:2020-09-09
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; }