【StarryCoding P101】排水管道 题解(单调栈+贪心算法)

45 阅读2分钟

[P101] 排水管道

问题描述

给定一个长度为 nn 的数组 aa ,请问至少修改多少个元素,可以使得数组成为一个严格上升的序列。

数组中需要时刻保持 ai>0a_i > 0

输入描述

第一行一个整数 TT 表示测试用例个数。 (1T1000)(1 \leq T \leq 1000)

对于每组测试用例,第一行一个整数 nn 表示数组中元素个数。 (1n105)(1 \leq n \leq 10^5)

第二行 nn 个整数表示表示数组 aa(1ai109)(1 \leq a_i \leq 10^9)

数据保证 n106\sum n \leq 10^6

输出描述

对于每组测试用例,一个整数表示答案。

输入样例

3
5
1 2 1 3 9
2
2 1
1
1

输出样例

2
1
0

思路

首先,定义一个大小为NN的数组aa和单调栈stkstk,以及栈顶指针toptopNN是预定义的数组和栈的最大容量,stkstk的作用是存储单调序列,toptop用于指示栈顶的位置。

在主函数中,首先读取测试用例的数量tt,然后对每个测试用例执行以下操作:

读取数组长度nn,然后读取nn个数组元素。对于每个元素a[i]a[i],将元素a[i]a[i]减去其索引ii,如果结果小于00,则跳过此元素。减去索引后的结果如果小于00,那么这个元素就不可能属于最终的上升序列。

接下来,如果栈为空,或者栈顶元素小于等于a[i]a[i],则将a[i]a[i]压入栈中。这是因为我们要构造的是一个严格上升的序列,所以新的元素必须大于前一个元素。

如果栈不为空且栈顶元素大于a[i]a[i],则在栈中找到第一个大于a[i]a[i]的元素,并将其替换为a[i]a[i]。利用贪心算法的思想,将大的元素替换为小的元素,可以增加后续元素被压入栈的可能性,从而尽可能地减少需要修改的元素数量。

最后,输出ntopn - top,这是因为toptop表示的是最长上升子序列的长度,而ntopn - 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]) {
				// 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;
}