题解
想想应该明白, (i < j < k) i -> j ->k, 必然能够直接 i -> k
那就是线性dp了, 从 1 一直到 n 点, dp就完事, 关键是如何转移,
或者说是 i 能到达哪些点?
想想应该明白, (i < j < k) i -> j ->k, 必然能够直接 i -> k
所以我们只需要求得
正解单调栈预处理, 没啥意思, 看了rk1的代码, 有点意思
很巧妙的处理方式,
和前几天写的, 拿到前缀和处理一样巧妙, 用后续节点去更新前面的节点
至于 max h[i + 1 ~ j - 1] < min(h[i], h[j]) 和 min h[i+ 1 ~ j - 1] > max(h[i], h[j]) 本质一样的
你把 h[i] = -h[i], 不就把 max 变成 min了, 说白了相同算法, h取负数算两次就行, 那我们直接考虑 max
只要按从到大小顺序取, 那么 对于 a < b 只要不存在 c(a < c < b) 比 a, b 先取出来(h[c] > min(h[a], h[b])) a -> b 就可以连接
如果存在这个c, 且不存在 d(a < d < c) 比 a, c 先取出来(h[d] > min(h[a], h[c])) a -> c就可以连接 ...(无限套娃)
按从大到小取出 b 时, 我们能直接得到 a (已经被取出, 且所有取出的数, a,b 距离最近) 就可以链接 a -> b,
用 set 维护 取出的集合就好了!
同时我们还要考虑 b(刚被取出) -> c(c已经被取出), 我们要确保没有 d(未被取出, h[d] == h[b], d < c), 就可以 b -> c
剩下的 dp就好了
#include <bits/stdc++.h>
#define all(n) (n).begin(), (n).end()
#define se second
#define fi first
#define pb push_back
#define mp make_pair
#define sqr(n) (n)*(n)
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<ll, ll> PLL;
typedef vector<int> VI;
typedef double db;
const int N = 1e4 + 5;
int n, m, _, k, x, y;
int main() {
IOS;
cin >> n; VI a(n), b(n);
vector<VI> e(n);
for (auto& i : a) cin >> i;
rep(i, 0, n - 1) b[i] = i;
rep(cas, 1, 2) {
for (auto& i : a) i = -i;
sort(all(b), [&](int& x, int& y) {
return tie(a[x], x) < tie(a[y], y);
});
set<int> st;
rep(i, 0, n - 1) {
st.insert(b[i]);
auto cur = st.find(b[i]), ne = next(cur);
if (cur != st.begin()) e[*prev(cur)].pb(b[i]);
if (ne != st.end() && (i + 1 == n ||
a[b[i + 1]] != a[b[i]] || b[i + 1] > * ne)) e[b[i]].pb(*ne);
}
}
VI f(n, 1e8); f[0] = 0;
rep(i, 0, n - 1)
for (auto& j : e[i]) f[j] = min(f[j], f[i] + 1);
cout << f[n - 1] << '
';
return 0;
}