1 条题解
-
0
题目题解
问题理解
给定数组 ,可以执行至多一次操作:选择 ,令 ,然后 。
$$S = \min(a_1) + \min(a_1, a_2) + \dots + \min(a_1, a_2, \dots, a_n). $$
目标是最小化前缀最小值之和:
第一步:分析前缀最小值序列
设 。
显然 ,且序列 是非递增的(因为取最小值只会变小或不变)。操作只会影响两个位置 和 ():
- 增加 ,可能使 变大(如果 原来是前缀最小值)。
- 变为 ,这会使 变为 (因为 是最小值)。
因此,操作的关键作用是将 及之后的所有前缀最小值变为 。
第二步:不操作的情况
若不操作,则 $S = a_1 + \min(a_1, a_2) + \dots + \min(a_1, \dots, a_n)$。
由于 固定,。
第三步:操作的可能效果
考虑操作 :
-
若 ,则 变为 ,且 。
$$S = (a_1 + a_j) + \min(a_1, a_2) + \dots + \min(a_1, a_{j-1}) + 0 + \dots + 0. $$
此时 , 不变(因为 变大不影响它们的最小值),。
总和变为:由于 (对 ),该和至少为 。
实际上,可以取到 (当 且 时,,总和为 )。 -
若 且 (),则 变为 ,。
此时 ,,。
总和为 。
若 ,则和为 ;否则为 。
第四步:最优策略
观察可知,最优操作总是涉及最小的几个下标,因为 会从 开始向后传播。
经过分析(可手动验证小 ),最优结果总是以下两者之一:
- 不操作:$S_0 = a_1 + \min(a_1, a_2) + \dots + \min(a_1, \dots, a_n)$。
- 操作 :(因为 ,,后面全 )。
- 操作 (当 且 ):。
进一步推导可知,对于所有情况,最小可能值就是 。
原因:
-
若 ,不操作时 ,,后面至少 ,总和 。
操作 可得 或更大,但无法小于 。
而操作 得到 ,所以最小为 (当 时,,所以 更小或相等)。 -
若 ,不操作时 ,,后面至少 ,总和 。
操作 得到 ,且 (因为 ),所以最小为 。
因此,答案统一为:
第五步:边界情况
-
当 时,无法操作 ,但上述公式仍然正确:
若 ,答案为 (操作 );
若 ,不操作得 ,操作 得 ,所以最小为 。 -
当 时,公式依然成立。
算法步骤
- 读入 和数组 。
- 输出 。
时间复杂度
- 每个测试用例 (忽略输入)。
- 总复杂度 ,满足 限制。
代码实现
#include <bits/stdc++.h> using namespace std; void solve() { int n; cin >> n; vector<int> a(n); for (int i = 0; i < n; i++) { cin >> a[i]; } cout << min(2 * a[0], a[0] + a[1]) << "\n"; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
验证样例
测试用例 输出 1 2 3 2 2 3 6 3 与题目输出一致。
总结
本题的关键在于:
- 理解操作如何影响前缀最小值序列。
- 发现只有前两个元素真正影响最优值。
- 得到简洁的闭式解 。
- 1
信息
- ID
- 6246
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 1
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者