1 条题解
-
0
C. 子数组最小值求和 题解
一、题意理解
我们有一个数据结构,支持以下五种操作:
pushback x:将值为 的元素添加到末尾pushfront x:将值为 的元素添加到开头popback:移除最后一个元素popfront:移除第一个元素min:将当前结构中的最小值累加到变量 上
目标:对任意给定的数组 (长度为 ),构造一系列操作,使得最终 等于该数组所有非空子数组的最小值之和:
$$\sum_{0 \leq l \leq r < n} \min(a[l], \dots, a[r]) $$操作总数不能超过 。
二、核心思路
我们需要遍历所有子数组 ,并对每个子数组调用一次
min操作。直观想法:利用数据结构模拟滑动窗口。如果我们能按某种顺序,让结构恰好依次包含每个子数组的元素,并在每次变化后调用
min,就能累加所有子数组的最小值。
三、算法设计
3.1 基本策略
我们从数组的两端向中间逐层处理。设当前处理范围是 (第 层)。
第 层( 从 开始):
-
如果 是偶数(从左向右扫描):
- 依次将 从前端加入结构,每次加入后调用
min。 - 这样可以得到所有以 为左端点、以 ()为右端点的子数组最小值。
- 扫描完后,从后端弹出一次(
popback),移除最右侧元素。
- 依次将 从前端加入结构,每次加入后调用
-
如果 是奇数(从右向左收尾):
- 此时结构中最右侧的元素是多余的,需要依次从前端弹出,并在每次弹出前调用
min。 - 这样可以得到所有以 ()为左端点、以某个位置为右端点的子数组最小值。
- 等价于将左边界向右收缩。
- 此时结构中最右侧的元素是多余的,需要依次从前端弹出,并在每次弹出前调用
3.2 交替扫描过程详解
以 为例,数组 。
第 层(,偶数,从左向右):
操作 结构内容 min 对应子数组 pushfront a[0]— minpushfront a[1]— minpushfront a[2]— minpopback— 此时结构内容为 ,对应子数组 。
第 层(,奇数,从右向左收缩):
操作 结构内容 min 对应子数组 minpopfront— minpopfront— 此时一共覆盖了所有子数组:、、、、。
四、标程解析
标准程序
pashka的实现非常简洁:private fun solveTest() { val n = readInt() val res = emptyList<String>().toMutableList() for (i in 0..n - 1) { if (i % 2 == 0) { for (j in i..n - 1) { res.add("pushfront a[" + j + "]") res.add("min") } res.add("popback") } else { for (j in i..n - 1) { res.add("min") res.add("popfront") } } } println(res.size) for (s in res) { println(s) } }代码逻辑:
-
外层循环 从 到 ,表示当前层的起始位置。
-
当 为偶数(从左向右扫描):
- 内层循环 从 到 :
- 执行
pushfront a[j],将右侧元素逐个从前方压入; - 紧接着执行
min,记录当前结构对应的子数组最小值。
- 执行
- 循环结束后执行一次
popback,移除最右侧的元素,为下一层做准备。
- 内层循环 从 到 :
-
当 为奇数(从右向左收缩):
- 内层循环 从 到 :
- 先执行
min,记录当前结构对应子数组的最小值; - 再执行
popfront,从前方移除元素,收缩左边界。
- 先执行
- 内层循环 从 到 :
五、操作次数分析
每一层 的操作次数:
- 偶数层: 次
pushfront+ 次min+ 次popback - 奇数层: 次
min+ 次popfront
总操作次数:
$$\begin{aligned} ops &= \sum_{i=0}^{n-1} \left[ 2(n-i) + (i \bmod 2 == 0\ ?\ 1 : 0) \right] \\[4pt] &= 2 \sum_{k=1}^{n} k + \lceil \frac{n}{2} \rceil \\[4pt] &= 2 \cdot \frac{n(n+1)}{2} + \lceil \frac{n}{2} \rceil \\[4pt] &= n(n+1) + \lceil \frac{n}{2} \rceil \end{aligned} $$对于 ,有:
因此操作数严格满足题目限制。
六、完整示例
样例 1:
3 pushback a[0] min popfront过程:
pushback a[0]→ 结构:min→ 累加 ,对应子数组popfront→ 结构清空
覆盖子数组 ,输出总数 ✅
样例 2:
6 pushfront a[1] min pushback a[0] min popfront min过程:
pushfront a[1]→ 结构:min→ 子数组pushback a[0]→ 结构:min→ 子数组 (即 )popfront→ 结构:min→ 子数组
覆盖所有子数组 、、,输出总数 ✅
七、代码实现(C++)
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; vector<string> res; for (int i = 0; i < n; i++) { if (i % 2 == 0) { // 偶数层:从左向右,将元素从前端加入 for (int j = i; j < n; j++) { res.push_back("pushfront a[" + to_string(j) + "]"); res.push_back("min"); } res.push_back("popback"); } else { // 奇数层:从右向左收缩,从前端弹出 for (int j = i; j < n; j++) { res.push_back("min"); res.push_back("popfront"); } } } cout << res.size() << "\n"; for (const string& s : res) { cout << s << "\n"; } return 0; }
八、复杂度分析
- 生成命令序列的时间复杂度为 ,空间复杂度也为 。
- 对于 ,操作数上限为 ,完全可行。
- 算法保证任意输入数组都能正确累加所有子数组的最小值。
- 1
信息
- ID
- 6654
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者