CF2205D
摘要:单调栈
前置芝士:洛谷单调栈模板题
题意转化
要求删除次数最少,相当于求出一个最长的 cool。
而显然:求最长的 cool 相当于是求最长的单谷序列。
因为多谷则必然有峰,那么还需再删;
而单调序列相当于一端点是谷,故也可以用单谷序列去考虑。
如此,原题题意就转化为求最长单谷序列的长度。
而答案最小删除次数则用原排列长度减去此值即可。
思路
那么如何求最长单谷序列的长度?
可以想到用单调栈完成。
- 先从左往右扫一遍,维护一个单减栈。
不难发现,当新加入的元素大于栈顶元素 \(a_x\) 时,栈内元素序列即是 \(a_x\) 左侧含 \(a_x\) 的最大单减序列,而栈的大小即是其长度。 - 再从右往左扫一遍,维护一个单减栈。
不难发现,当新加入的元素大于栈顶元素 \(a_x\) 时,栈内元素序列即是 \(a_x\) 右侧含 \(a_x\) 的最大单增序列,而栈的大小即是其长度。
对于一个元素 \(a_x\) 而言,把上述的两种序列拼起来,可见就得到了 \(a_x\) 为谷的最大单谷序列。
由于 \(a_x\) 被数了两次,所以 \(a_x\) 为谷的最长单谷序列长度 \(mxl_x = dl_x + dr_x - 1\)。
注意别忘了每次最后还要处理留在栈中的元素。
那么就做完了。
举例
这么讲可能有点抽象。我以样例中的一组数据大概模拟一遍该过程。
样例中的一组数据是这样的:
4 1 3 2 5
-
从左到右扫,维护单减栈。
- 压入 \(4\),栈内
4。 - 压入 \(1\),栈内
4 1。 - 压入 \(3\)。
\(3 > 1\),所以弹出 \(1\)。
可见此时栈内的4 1就是 \(1\) 左侧含 \(1\) 的最大单减序列。求出相应的有关值。
栈内4 3。 - 压入 \(2\),栈内
4 3 2。 - 压入 \(5\),栈内所有元素均比它小,所以都弹出,并求出相关对应值。
栈内5。
最后弹出栈中所有元素。与前面逻辑同理,此时的每一个元素的 \(dl_x\) 值仍为它即将弹出前的栈的大小。
- 压入 \(4\),栈内
-
从右往左扫,维护单减栈。
逻辑也是一样的,这里就不赘述了。
代码
有注释版本
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int T;
int n, a[N];
int s[N], tops;
int dl[N], dr[N];
int ans;
int main(){
scanf("%d", &T);
while(T--){
scanf("%d", &n);
for(int i = 1; i <= n; ++i){
scanf("%d", &a[i]);
}
tops = 0;
for(int i = 1; i <= n; ++i){
if(tops < 1){
s[++tops] = i;
continue ;
}
while(a[s[tops]] < a[i] && tops > 0){
dl[s[tops]] = tops;
--tops;
}
s[++tops] = i;
}
while(tops > 0){
dl[s[tops]] = tops;
--tops;
}
tops = 0;
for(int i = n; i >= 1; --i){
if(tops < 1){
s[++tops] = i;
continue ;
}
while(a[s[tops]] < a[i] && tops > 0){
dr[s[tops]] = tops;
--tops;
}
s[++tops] = i;
}
while(tops > 0){
dr[s[tops]] = tops;
--tops;
}
int mxl = -1;
for(int i = 1; i <= n; ++i){
mxl = max(mxl, dl[i] + dr[i] - 1);
}
ans = n - mxl;
printf("%d\n", ans);
}
return 0;
}
无注释版本
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int T;
int n, a[N];
int s[N], tops;
int dl[N], dr[N];
int ans;
int main(){
scanf("%d", &T);
while(T--){
scanf("%d", &n); //输入。scanf真曼妙。
for(int i = 1; i <= n; ++i){
scanf("%d", &a[i]);
}
tops = 0;
for(int i = 1; i <= n; ++i){ //从左到右扫,维护单减栈
if(tops < 1){
s[++tops] = i;
continue ;
}
while(a[s[tops]] < a[i] && tops > 0){ //记得判断tops
dl[s[tops]] = tops;
--tops;
}
s[++tops] = i;
}
while(tops > 0){ //记得处理栈中剩余元素
dl[s[tops]] = tops;
--tops;
}
tops = 0;
for(int i = n; i >= 1; --i){ //从右到左扫,维护单减栈
if(tops < 1){
s[++tops] = i;
continue ;
}
while(a[s[tops]] < a[i] && tops > 0){ //记得判断tops
dr[s[tops]] = tops;
--tops;
}
s[++tops] = i;
}
while(tops > 0){ //记得处理栈中剩余元素
dr[s[tops]] = tops;
--tops;
}
int mxl = -1;
for(int i = 1; i <= n; ++i){
mxl = max(mxl, dl[i] + dr[i] - 1);
}
ans = n - mxl; //计算答案
printf("%d\n", ans); //输出。printf真曼妙。
}
return 0;
}
浙公网安备 33010602011771号