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;
}