1.题目
问题描述
在番茄小说的书籍中,编辑小S正在寻找精彩的连续章节以进行特色展示。每个章节都有各自的文字数量,编辑希望选出的连续章节总字数不超过 k
。在这些连续章节中,除了第一章和最后一章,任何字数多于前后章节的章节都被视为优质章节。编辑的目标是挑选出尽可能多的优质章节,同时满足总字数限制。如果有多个答案优质章节数相同,请输出总字数最少的答案。优质章节数和总字数均相同,则输出区间下标最小的答案。
例如,假设章节字数为 [1000, 3000, 2000, 4000, 3000, 2000, 4000, 2000]
,给定的字数上限 k
为 15000。一种优选方案是选择从第1章到第5章,其中第2章和第4章是优质章节。
输入:
n
:章节数目k
:总字数上限array_a
:各个章节的字数
返回规则如下:
- 返回一个字符串,格式为
"优质章节数,优质章节数最多的区间左下标,优质章节数最多的区间右下标"
测试样例
样例1:
输入:n = 8,k = 15000,array_a = [1000, 3000, 2000, 4000, 3000, 2000, 4000, 2000]
输出:
'2,1,5'
样例2:
输入:n = 8,k = 15000,array_a = [2000, 5000, 2000, 1000, 4000, 2000, 4000, 3000]
输出:
'2,4,8'
样例3:
输入:n = 5,k = 10000,array_a = [3000, 4000, 1000, 5000, 2000]
输出:
'1,1,3'
样例4:
输入:n = 6,k = 8000,array_a = [1000, 2000, 3000, 4000, 500, 2500]
输出:
'1,3,5'
样例5:
输入:n = 10,k = 5000,array_a = [500, 1000, 1500, 500, 500, 1000, 1500, 500, 500, 1000]
输出:
'1,2,4'
2.思路
- 计算前缀和(
sum_array
):- 由于我们需要计算某一连续区间内的字数总和,所以我们可以用前缀和数组
sum_array
来快速计算任意区间的字数总和。 sum_array[i]
表示从第1章到第i章的字数累计和。这样,区间array_a[left]
到array_a[right]
的总字数可以通过sum_array[right] - sum_array[left - 1]
来快速得到。
- 由于我们需要计算某一连续区间内的字数总和,所以我们可以用前缀和数组
- 遍历章节:
- 从第二章到倒数第二章(因为第一章和最后一章无法成为优质章节,缺少前后章节的比较)进行遍历。
- 对于每一章,判断它是否是一个局部最大值,即它的字数是否大于前一章和后一章。如果是局部最大值,说明这章是一个优质章节。
- 队列的使用:
- 使用
deque
(双端队列)来存储当前可能的优质章节区间索引。 - 为了确保区间总字数不超过上限
k
,当队列中的章节字数和超过k
时,我们会从队列的头部移除一些章节,直到总字数满足要求。
- 使用
- 更新最优解:
- 每次更新队列时,检查当前区间的优质章节数量和总字数:
- 如果当前区间的优质章节数比之前的区间多,则更新最优解。
- 如果优质章节数相同,则比较总字数,选取字数最少的区间。
- 如果优质章节数和总字数都相同,则选择左端下标最小的区间。
- 每次更新队列时,检查当前区间的优质章节数量和总字数:
- 最终返回结果:
- 遍历完所有章节后,返回优质章节数、区间的左端下标和右端下标。
3.代码
#include <iostream>
#include <vector>
#include <string>
#include <deque>
#include <cassert>
using namespace std;
std::string solution(int n, int k, std::vector<int> array_a) {
// Please write your code here
// 校验输入的合法性
assert(n == array_a.size());
assert(3 <= n && n <= 100000);
assert(0 <= k && k <= 1000000000);
// 初始化一个数组sum_array,用于存储从第1章到第i章的字数累计和
vector<int> sum_array(n, 0);
sum_array[0] = array_a[0];
for (int i = 1; i < n; i++) {
sum_array[i] = sum_array[i - 1] + array_a[i];
// 校验每章的字数是否合法
assert(1 <= array_a[i] && array_a[i] <= 10000000);
}
// 定义一个lambda表达式,用于获取从第left章到第right章的字数之和
auto get_sum_from_a_to_b = [&](int left, int right) {
return(sum_array[right] - (left > 0 ? sum_array[left - 1] : 0));
};
// 使用队列deque来存储可能的优质章节索引
deque<int> q;
int ans = 0, start = -1, end = -1, total_size = -1; // 初始化答案变量
// 遍历每章,寻找优质章节
for (int i = 1; i < n - 1; ++i) {
// 判断当前章节是否是局部最大值(优质章节的定义)
if (array_a[i] > array_a[i - 1] && array_a[i] > array_a[i + 1]) {
q.push_back(i);
// 如果队列中的章节总字数超过k,则从队列头部移除章节,直到满足字数限制
while (!q.empty() && get_sum_from_a_to_b(q.front() - 1, q.back() + 1) > k) {
q.pop_front();
}
// 如果队列非空,则检查当前的区间并更新答案
if (!q.empty()) {
int current_size = get_sum_from_a_to_b(q.front() - 1, q.back() + 1);
// 如果当前区间优质章节数更多,或优质章节数相同但字数更少,更新答案
if (q.size() > ans || (q.size() == ans && total_size > current_size)) {
ans = q.size();
start = q.front() - 1;
end = q.back() + 1;
total_size = current_size;
}
}
}
}
// 确保有有效的答案
assert (ans != 0);
// 返回符合要求的字符串,格式为 "优质章节数,区间左端下标,区间右端下标"
return to_string(ans) + "," + to_string(start + 1) + "," + to_string(end + 1);
}
int main() {
// You can add more test cases here
std::vector<int> array_a1 = {1000, 3000, 2000, 4000, 3000, 2000, 4000, 2000};
std::vector<int> array_a2 = {2000, 5000, 2000, 1000, 4000, 2000, 4000, 3000};
std::cout << (solution(8, 15000, array_a1) == "2,1,5") << std::endl;
std::cout << (solution(8, 15000, array_a2) == "2,4,8") << std::endl;
return 0;
}