CSP-202109-2-非零段划分
【70分思路-暴力枚举】
这段代码的目的是在给定一个由自然数(非负整数)组成的数组后,通过选择一个适当的正整数 p,将数组中所有小于 p 的数变为 0,从而使得数组中非零段的数量达到最大。这里的非零段是指连续的、非零的数组元素序列。
程序的主要逻辑分为以下几个步骤:
-
读取数组长度 n 和数组元素,同时找出数组中的最大元素 maxElem。
-
对于每一个可能的 p 值(从 1 到 maxElem),复制原始数组并将所有小于 p 的元素设置为 0。
-
对于每个 p 值的新数组,遍历数组来计算非零段的数量。一个非零段开始于一个非零元素,该元素要么是数组的第一个元素,要么其前一个元素为零。非零段结束于数组的最后一个元素或一个非零元素后跟着一个零元素。
-
更新并记录非零段数量的最大值。
-
输出非零段的最大数量。
时间复杂度
-
第一层循环(读取数组)的时间复杂度为 O(n),n 是数组的长度。
-
第二层循环是对于每一个可能的 p 值进行迭代,其最坏情况下的时间复杂度为 O(maxElem)。
-
在每一个 p 的值下,我们又对数组进行了两次遍历(一次是将小于 p 的值置为 0,另一次是计算非零段的数量),每次遍历的时间复杂度为 O(n)。
因此,整个程序的总时间复杂度为 O(maxElem * n),这里 maxElem 是数组中的最大值,n 是数组的长度。由于 maxElem 可能接近 n,所以在最坏情况下,时间复杂度可以近似为 O(n^2)。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<int>arr;int main() {long long n;int maxNum = -1, maxElem = -1;cin >> n;for (int i = 0; i < n; i++){int t;cin >> t;arr.push_back(t);maxElem = max(maxElem, t);}for (int p = 1; p <= maxElem; p++){// 小于 p 的数都变为 0for (auto& it : arr) {if (it < p) it = 0;}int num = 0;bool flag = 1; // 1-上一位是0;0-上一位不是零// 统计非零段for (auto& it : arr) {if (it != 0) {if (flag) {num++;}flag = 0;}else{flag = 1;}}// 记录最大非零段数maxNum = max(maxNum, num);}cout << maxNum;return 0;
}
【100分思路-差分数组】
-
初始化和输入处理:定义了两个向量
numbers
和diff
,分别用于存储输入的数列和差分数组。numbers
的大小比实际数列长度多2,这是为了在数列的开始和结束添加边界值0,以方便处理。 -
去重和边界处理:使用
unique
函数去除连续的重复元素,这对于减少不必要的计算特别有效,因为连续的相同数值不会增加非零段的数量。 -
差分数组的构建:
- 差分数组
diff
用于记录每个可能的数值对应的变化(峰值增加,谷值减少)。这实际上是对数列进行一种“转化”,使得后续的求解更加直接和高效。 - 遍历数列,如果当前数字是一个峰值(即比前一个和后一个数都大),则在差分数组对应位置加一;如果是谷值(即比前一个和后一个数都小),则减一。
- 差分数组
-
通过差分数组求解答案:
- 通过遍历差分数组的累加和(即从后向前计算前缀和),可以找出使非零段数量最大化的数值。这是因为差分数组的前缀和反映了在当前阈值下,非零段的增减情况。
时间复杂度
- 初始化和输入处理:O(N),其中N是数列的长度。
- 去除连续重复元素:最坏情况下O(N),因为需要检查每个元素是否与前一个相同。
- 构建差分数组:O(N),每个元素至多被访问一次。
- 通过差分数组求解答案:O(V),其中V是数值的最大可能值,这里是
MAX_VALUE
。 - 因此,总体时间复杂度为O(N + V),其中N是数列的长度,V是数值的最大可能范围。
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;const int MAX_NUM = 500000; // 最大数字数量
const int MAX_VALUE = 10000; // 最大值
vector<int> numbers(MAX_NUM + 2); // 使用vector存储输入的数列
vector<int> diff(MAX_VALUE + 1); // 使用vector存储差分数组int main() {int length;cin >> length;for (int i = 1; i <= length; i++) {cin >> numbers[i];}numbers[0] = numbers[length + 1] = 0; // 将边界设置为0// unique函数去除连续重复元素,更新vector的有效长度length = unique(numbers.begin(), numbers.begin() + length + 2) - numbers.begin() - 1;// 初始化差分数组为0fill(diff.begin(), diff.end(), 0);for (int i = 1; i < length; i++){if (numbers[i - 1] < numbers[i] && numbers[i] > numbers[i + 1]) {diff[numbers[i]]++; // 如果是峰值,对应的差分数组加一}else if (numbers[i - 1] > numbers[i] && numbers[i] < numbers[i + 1]) {diff[numbers[i]]--; // 如果是谷值,对应的差分数组减一}}// 通过差分数组求解答案int maxSegments = 0, sum = 0; // maxSegments记录最终答案,sum记录差分的前缀和for (int i = MAX_VALUE; i >= 1; i--) {sum += diff[i]; // 累加差分得到前缀和maxSegments = max(maxSegments, sum); // 更新答案}cout << maxSegments << endl; return 0;
}