gk的爬山之旅(单调栈 DP)
题目:
有n座山峰,每个山峰都有自己的高度和值,现在出题人要在群山中穿梭。穿梭有两个条件:1 他们只会去高度大于当前所在山峰的高度的山峰;2 他们只能到达左右两边第一个比自己高的山峰。
思路:
整理题意后明显可以发现是一个单调栈题目,对于每个i,预处理出左右第一个可达的山峰,就可以将题目转化成一个DAG上的最长路问题,可以通过记忆化搜索的方式解出。
实现:
在实现的过程中,样例无法通过,原来是疏忽了一个条件。因为山峰的值数组是严格递增或者严格递减的,所以对于 8 8 8 8 6,我们应该从6跳到最左边的8,才能获得最大的收益。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, n) for(int i = a; i < n; i++)
#define all(x) x.begin(), x.end()
#define pb push_back
#define ios ios::sync_with_stdio(false);cin.tie(0);
#define debug(x) cout << x << endl;
#define SZ(x) (int)x.size()
typedef long long ll;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f;
void read(int &x) {int s = 0, f = 1; char ch = getchar(); while(!isdigit(ch)) {f = (ch == '-' ? -1 : f); ch = getchar();} while(isdigit(ch)) {s = s * 10 + ch - '0'; ch = getchar();} x = s * f;}
const int N = 1000005;
int n;
ll f[N];
int h[N], g[N];
int l[N], r[N]; //左边第一个比h[i]大的,右边第一个比h[i]大的
vector<int> G[N];
void init()
{
for(int i = 1; i <= n; i ++)
f[i] = -1, G[i].clear(), l[i] = r[i] = -1;
}
void dfs(int u) //DAG上DP
{
if(f[u] != -1) return;
f[u] = 0;
for(int v : G[u])
{
if(f[v] == -1) dfs(v);
f[u] = max(f[u], f[v] + abs(g[u] - g[v]));
}
}
void solve()
{
scanf("%d", &n);
init();
for(int i = 1; i <= n; i ++)
scanf("%d", &h[i]);
for(int i = 1; i <= n; i ++)
scanf("%d", &g[i]);
stack<int> stk1, stk2;
int idx;
for(int i = 1; i <= n; i ++)
{
idx = i;
while(stk1.size() && h[stk1.top()] <= h[i])
{
if(h[i] == h[stk1.top()]) idx = stk1.top();
stk1.pop();
}
if(stk1.size()) l[i] = stk1.top();
stk1.push(idx);
}
for(int i = n; i >= 1; i --)
{
idx = i;
while(stk2.size() && h[stk2.top()] <= h[i])
{
if(h[i] == h[stk2.top()]) idx = stk2.top();
stk2.pop();
}
if(stk2.size()) r[i] = stk2.top();
stk2.push(idx);
}
for(int i = 1; i <= n; i ++)
{
if(l[i] != -1) G[i].pb(l[i]);
if(r[i] != -1) G[i].pb(r[i]);
}
for(int i = 1; i <= n; i ++)
dfs(i);
for(int i = 1; i <= n; i ++)
printf("%lld ", f[i]);
puts("");
}
signed main()
{
int _ = 1;
scanf("%d", &_);
while(_--)
solve();
}