1 条题解
-
0
题意重述
给定一个由
$$a_i = \begin{cases} 1, & s_i = \text{`+'}\\ -1, & s_i = \text{`-'} \end{cases} $$"+"和"-"组成的字符串 ,长度 。
定义数组 :我们要将 分成若干个非空连续子数组 (即 , 表示数组连接)。
对每个子数组 (长度为 )定义罚分:
总罚分:
问:在最优拆分下, 的最小值是多少。
关键观察
1. 罚分的下界
定义一个替代罚分函数:
即忽略长度因子。
显然:
因为 当 。
对于替代罚分函数,总罚分为 。
$$\min \sum |\text{subarray sum}| = |a_1 + a_2 + \dots + a_n| $$
由于对整数 有 ,因此最优策略是不拆,直接取整个数组:因此原问题的答案 。
2. 可达性证明
我们想证明:存在一种拆分,使 。
第一步:对称性
若把 中所有元素取反(+变-,-变+),每个子数组的绝对值不变,所以答案不变。
因此可假设 。
第二步:构造
-
若 ,直接整个数组作为一个子数组,罚分 ,达到下界 。
-
若 :
寻找最大的 ,使前缀和 。
将 作为一个子数组,它的和是 ,罚分 。
下一个元素 必须是 (否则若它是 ,从 开始再出现负值,为了最终和 ,后面必须出现更大的正和,会使得另一个前缀和再次为 ,这与 最大矛盾)。所以 。
我们将 作为一个长度为 的子数组,其和为 ,罚分 。剩余部分 的总和变为 。
重复上述过程:每次切出一个 和的子数组(可能空)和一个 单元素子数组,总罚分增加 ,剩余总和减少 ,直到总和减到 。
最终总罚分 。
3. 总结公式
因此,最小罚分就是整个数组的总和的绝对值:
其中 对应
'+', 对应'-'。
代码实现
#include <bits/stdc++.h> using namespace std; int main() { int t; cin >> t; while (t--) { int n; string s; cin >> n >> s; int sum = 0; for (char c : s) { sum += (c == '+') ? 1 : -1; } cout << abs(sum) << '\n'; } return 0; }
复杂度分析
- 时间复杂度: 对每个测试用例。
- 空间复杂度: 除了存储字符串。
验证样例
"+":,总和 ,答案 ✅"-----":,总和 ,绝对值 ✅"+-+-+-":,总和 ,答案 ✅"--+++++++-":,总和 ,答案 ✅"+---++++-+++++---++-":计算可得总和绝对值 ✅
-
- 1
信息
- ID
- 6437
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 1
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者