Psychos in a Line:很好的单调栈优化 dp 题!
观察
我们先观察,一个精神病人会一直杀到什么时候。显然,会杀到右边第一个比他大的精神病人那里,然后他就杀不动了。
因此我们可以从右往左考虑,算出左边的精神病人杀掉这个精神病人后左边的人的答案是什么。假设左边的人目前已经刀了 \(x\) 个人,被杀的人目前会刀 \(y\) 个人。如果左边杀这个人的时候这个人该杀的还没杀完,那么左边人就要接着这个人继续把该杀的杀掉,又因为它们刀人是同步进行的,所以此时左边的人目前刀掉的人数取 \(\max(y,x+1)\)。
于是,我们定义 \(dp_i\) 表示第 \(i\) 个人目前会刀几个人,然后 \(O(n^2)\) 转移即可。
优化
但是这样显然无法通过,考虑如何优化。
因为一个人会被他左边第一个比他大的人先杀掉(不一定最后真的是被他杀的,如果他杀这个人之前就被杀了,那么情况还是等价的,不影响计算),所以我们从右到左维护一个单调栈,维护右边的最大值。在一个元素入栈的时候,只取弹出的元素转移即可。
时间复杂度 \(O(n)\),主要还是理解一个人先钦定被左边第一个比他大的人杀,之后再动态调整,不影响最终答案的类似反悔贪心的思想。
代码
#include <bits/stdc++.h>
using namespace std;
int n,a[100005],dp[100005],tp=0,s[100005],ans;
int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n;for(int i=1;i<=n;i++)cin>>a[i];for(int i=n;i>=1;i--){int res=0;while(tp&&a[s[tp]]<a[i])res=max(res+1,dp[s[tp--]]);s[++tp]=i;dp[i]=res;ans=max(dp[i],ans);}cout<<ans;return 0;
}