tags
思维
动态规划
单调栈
中文题面
有 n 个心理学家站成一排。每个心理学家都被分配了一个从 1 到 n 的唯一整数。在每一步中,每个id 大于其右边的心理学家(如果存在)的心理学家都会杀死队伍中右边的邻居。请注意,一个心理学家可能会在同一步中杀人和被杀。
给你的是心理学家在这条直线上的初始排列。请计算到某一时刻需要走多少步才能保证在该时刻之后没有人杀死他的邻居。查看注释,以便更准确地理解这句话。
输入
第一行输入包含整数 n ,表示心理学家的数量 (1 ≤ n ≤ 105) 。第二行是由 n 个空格分隔的不同整数组成的列表,每个整数的范围在 1 到 n 之间,从左到右依次为该行中心理学家的id。
输出
打印步数,以便之后的行保持不变。
思路
刚开始想模拟每一轮次的kill,粗略证明以为复杂度是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),结果超时,实际最坏时间复杂度应该是
O
(
n
2
)
O(n^2)
O(n2)
接着尝试以
d
p
[
i
]
dp[i]
dp[i]代表第
i
i
i个人死亡时间(特别地我们认为始终无法被杀死的人在0时刻就死了),一个人如果想被杀死就需要向左找到第一个比它更大的数,暗合单调栈的思想,对于左边第一个就是比它更大的数的人在1时刻会被杀死,否则会在它与左边第一个更大数之间所有位置中最大时刻+1时刻被杀死(解释:因为只能杀相邻的人,这个人只能等到左边第一个更大数和它相邻后才会在下一时刻死亡,相邻的所需时间就是更大数杀光它们之间所有人的时间,而击杀的时刻就是在此基础上+1)
因此运用单调栈和动态规划解决
代码
#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
// Edit by Mr_Way
int n, dp[maxn];
int a[maxn], mx = 0;
stack<int> s;
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) {
while (!s.empty() && a[i] >= a[s.top()]) {
dp[i] = max(dp[i], dp[s.top()] + 1);
s.pop();
}
if (!s.empty() && s.top() == i - 1) dp[i] = 1;
else if (s.empty()) dp[i] = 0;
mx = max(dp[i], mx);
s.push(i);
}
cout << mx;
return 0;
}