[P101] 排水管道
问题描述
给定一个长度为 的数组 ,请问至少修改多少个元素,可以使得数组成为一个严格上升的序列。
数组中需要时刻保持 。
输入描述
第一行一个整数 表示测试用例个数。
对于每组测试用例,第一行一个整数 表示数组中元素个数。
第二行 个整数表示表示数组 。
数据保证 。
输出描述
对于每组测试用例,一个整数表示答案。
输入样例
3
5
1 2 1 3 9
2
2 1
1
1
输出样例
2
1
0
思路
首先,定义一个大小为的数组和单调栈,以及栈顶指针。是预定义的数组和栈的最大容量,的作用是存储单调序列,用于指示栈顶的位置。
在主函数中,首先读取测试用例的数量,然后对每个测试用例执行以下操作:
读取数组长度,然后读取个数组元素。对于每个元素,将元素减去其索引,如果结果小于,则跳过此元素。减去索引后的结果如果小于,那么这个元素就不可能属于最终的上升序列。
接下来,如果栈为空,或者栈顶元素小于等于,则将压入栈中。这是因为我们要构造的是一个严格上升的序列,所以新的元素必须大于前一个元素。
如果栈不为空且栈顶元素大于,则在栈中找到第一个大于的元素,并将其替换为。利用贪心算法的思想,将大的元素替换为小的元素,可以增加后续元素被压入栈的可能性,从而尽可能地减少需要修改的元素数量。
最后,输出,这是因为表示的是最长上升子序列的长度,而就是需要修改的元素数量。
AC代码
#include <algorithm>
#include <iostream>
#define mp make_pair
#define AUTHOR "HEX9CF"
using namespace std;
using ll = long long;
const int N = 1e6 + 7;
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
int t, n;
int a[N];
int top = 0;
int stk[N];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> t;
while (t--) {
top = 0;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
a[i] -= i;
if (a[i] < 0) {
// a[i]必须大于等于i才能严格上升
continue;
}
if (!top || stk[top] <= a[i]) {
// push
stk[++top] = a[i];
} else {
// 替换栈中第一个比a[i]大的元素
int pos = upper_bound(stk + 1, stk + 1 + top, a[i]) - stk;
stk[pos] = a[i];
}
}
cout << (n - top) << endl;
}
return 0;
}