[P101] 排水管道
问题描述
给定一个长度为 n n n 的数组 a a a ,请问至少修改多少个元素,可以使得数组成为一个严格上升的序列。
数组中需要时刻保持 a i > 0 a_i > 0 ai>0。
输入描述
第一行一个整数 T T T 表示测试用例个数。 ( 1 ≤ T ≤ 1000 ) (1 \leq T \leq 1000) (1≤T≤1000)
对于每组测试用例,第一行一个整数 n n n 表示数组中元素个数。 ( 1 ≤ n ≤ 1 0 5 ) (1 \leq n \leq 10^5) (1≤n≤105)
第二行 n n n 个整数表示表示数组 a a a 。 ( 1 ≤ a i ≤ 1 0 9 ) (1 \leq a_i \leq 10^9) (1≤ai≤109)
数据保证 ∑ n ≤ 1 0 6 \sum n \leq 10^6 ∑n≤106 。
输出描述
对于每组测试用例,一个整数表示答案。
输入样例
3
5
1 2 1 3 9
2
2 1
1
1
输出样例
2
1
0
思路
首先,定义一个大小为 N N N的数组 a a a和单调栈 s t k stk stk,以及栈顶指针 t o p top top。 N N N是预定义的数组和栈的最大容量, s t k stk stk的作用是存储单调序列, t o p top top用于指示栈顶的位置。
在主函数中,首先读取测试用例的数量 t t t,然后对每个测试用例执行以下操作:
读取数组长度 n n n,然后读取 n n n个数组元素。对于每个元素 a [ i ] a[i] a[i],将元素 a [ i ] a[i] a[i]减去其索引 i i i,如果结果小于 0 0 0,则跳过此元素。减去索引后的结果如果小于 0 0 0,那么这个元素就不可能属于最终的上升序列。
接下来,如果栈为空,或者栈顶元素小于等于 a [ i ] a[i] a[i],则将 a [ i ] a[i] a[i]压入栈中。这是因为我们要构造的是一个严格上升的序列,所以新的元素必须大于前一个元素。
如果栈不为空且栈顶元素大于 a [ i ] a[i] a[i],则在栈中找到第一个大于 a [ i ] a[i] a[i]的元素,并将其替换为 a [ i ] a[i] a[i]。利用贪心算法的思想,将大的元素替换为小的元素,可以增加后续元素被压入栈的可能性,从而尽可能地减少需要修改的元素数量。
最后,输出 n − t o p n - top n−top,这是因为 t o p top top表示的是最长上升子序列的长度,而 n − t o p n - top n−top就是需要修改的元素数量。
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]) {// pushstk[++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;
}