89.迷人子序列计数问题|Marscode AI刷题
1.题目
问题描述
当一个数列,最大值和最小值的差低于某一阈值时,称这个数列是迷人数列。
现给定一个由 n
个整数构成的数列和阈值 k
,问存在多少个连续子序列是迷人的。
输入格式
第一行包含 2 个数字 n, k
(1 <= n <= 100000
, 0 < k <= 10^9
)
第二行包含 n
个整数:a[1], a[2],..., a[n]
(0 <= a[i] <= 10^9
)
输出格式
输出迷人连续子序列的数目
输入样例
4 2
3 1 2 4
输出样例
5
数据范围
共 10 组数据。2 组数据 n
小于 1000;其余数据 n
等于 100000。
2.样例解析
输入样例
4 2
3 1 2 4
输出样例
5
当一个数列,最大值和最小值的差小于等于某一阈值时,称这个数列是迷人数列,此处的输出应该是8。
8个迷人子序列为:{3},{1},{2},{4},{3,1}{1,2},{2,4},{3,1,2}
3.思路
由于我们需要找到的是满足 max(a[i...j]) - min(a[i...j]) < k
的所有连续子序列,可以考虑使用 滑动窗口(即双指针法)来解决。
- 我们使用两个指针
left
和right
,维护一个当前的子序列a[left...right]
,并逐步扩展右指针。 - 当
max(a[left...right]) - min(a[left...right])
满足条件时,我们可以将子序列的左指针left
继续向右移动,直到不满足条件为止。 - 统计符合条件的连续子序列的数量。
4.代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int solution(int n, int k, std::vector<int> sequence) {
// Please write your code here
int left = 0;
int right = 0;
int result = 0;
int min_val = sequence[0];
int max_val = sequence[0];
while (left < n && right < n && left <= right) {
// 更新当前窗口的最大值和最小值(右移之后)
min_val = min(min_val, sequence[right]);
max_val = max(max_val, sequence[right]);
if ((max_val - min_val) < k) { // 符合条件,右指针右移
result++;
right++;
}else { //不符合条件,左指针右移
left++;
//更新当前窗口的最大值和最小值
min_val = *min_element(sequence.begin() + left, sequence.begin() + right + 1);
max_val = *max_element(sequence.begin() + left, sequence.begin() + right + 1);
}
}
return result;
}
int main() {
// You can add more test cases here
std::vector<int> sequence1 = {3, 1, 2, 4};
std::vector<int> sequence2 = {7, 3, 5, 1, 9};
std::vector<int> sequence3 = {2, 2, 3, 1, 1, 2};
std::cout << (solution(4, 2, sequence1) == 8) << std::endl;
std::cout << (solution(5, 3, sequence2) == 6) << std::endl;
std::cout << (solution(6, 1, sequence3) == 12) << std::endl;
return 0;
}
代码修改:
- result++改为result += (right - left + 1)
- (max_val - min_val)< k改为(max_val - min_val) ≤ k
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int solution(int n, int k, std::vector<int> sequence) {
// Please write your code here
int left = 0;
int right = 0;
int result = 0;
int min_val = sequence[0];
int max_val = sequence[0];
while (left < n && right < n && left <= right) {
// 更新当前窗口的最大值和最小值(右移之后)
min_val = min(min_val, sequence[right]);
max_val = max(max_val, sequence[right]);
if ((max_val - min_val) <= k) { // 符合条件,右指针右移
result += (right - left + 1);
right++;
}else { //不符合条件,左指针右移
left++;
//更新当前窗口的最大值和最小值
min_val = *min_element(sequence.begin() + left, sequence.begin() + right + 1);
max_val = *max_element(sequence.begin() + left, sequence.begin() + right + 1);
}
}
return result;
}
int main() {
// You can add more test cases here
std::vector<int> sequence1 = {3, 1, 2, 4};
std::vector<int> sequence2 = {7, 3, 5, 1, 9};
std::vector<int> sequence3 = {2, 2, 3, 1, 1, 2};
std::cout << (solution(4, 2, sequence1) == 8) << std::endl;
std::cout << (solution(5, 3, sequence2) == 6) << std::endl;
std::cout << (solution(6, 1, sequence3) == 12) << std::endl;
return 0;
}
代码详解:
result += (right - left + 1)
对于每个 right
,如果窗口 [left, right]
的最大值和最小值之差小于等于 k
,则此窗口中的所有子序列都符合条件。
具体的子序列数量:
-
对于每个固定的
right
,所有以left
到right
为子序列的组合都满足条件。 -
这些子序列的个数就是窗口内的所有可能的子序列数,即从
left
到right
形成的连续子序列。 -
例如,如果窗口是
[left, right]
,那么所有可能的子序列是:sequence[left]
sequence[left+1]
sequence[left+2]
- ...
sequence[right]
这些子序列的数量就是
(right - left + 1)
。因此,每次窗口扩展时,我们就可以通过result += (right - left + 1)
来累加符合条件的子序列数量。
min_val = *std::min_element(sequence.begin() + left, sequence.begin() + right + 1);
max_val = *std::max_element(sequence.begin() + left, sequence.begin() + right + 1);
std::min_element
:- 这个函数返回指定范围内的最小元素的迭代器。
- 语法:
std::min_element(first, last)
first
:指向范围的起始位置的迭代器。last
:指向范围的结束位置的迭代器(不包括该位置)。
- 返回值:指向范围内最小元素的迭代器。
std::max_element
:- 这个函数返回指定范围内的最大元素的迭代器。
- 语法:
std::max_element(first, last)
first
:指向范围的起始位置的迭代器。last
:指向范围的结束位置的迭代器(不包括该位置)。
- 返回值:指向范围内最大元素的迭代器。
std::min_element
和std::max_element
是 C++ 标准库中的函数,用于在给定的范围内找到最小值和最大值的迭代器。为了获取这些迭代器指向的实际值,你需要对它们进行解引用操作,即使用*
运算符。