1 条题解

  • 0
    @ 2026-4-6 12:10:46

    题意重述

    给定一个由 "+""-" 组成的字符串 ss,长度 nn
    定义数组 aa

    $$a_i = \begin{cases} 1, & s_i = \text{`+'}\\ -1, & s_i = \text{`-'} \end{cases} $$

    我们要将 aa 分成若干个非空连续子数组 b1,b2,,bkb_1, b_2, \dots, b_k(即 b1+b2++bk=ab_1 + b_2 + \dots + b_k = a++ 表示数组连接)。

    对每个子数组 cc(长度为 mm)定义罚分:

    p(c)=c1+c2++cmmp(c) = |c_1 + c_2 + \dots + c_m| \cdot m

    总罚分:

    P=p(b1)+p(b2)++p(bk)P = p(b_1) + p(b_2) + \dots + p(b_k)

    问:在最优拆分下,PP 的最小值是多少。


    关键观察

    1. 罚分的下界

    定义一个替代罚分函数

    p2(l,r)=j=lrajp_2(l, r) = \left| \sum_{j=l}^r a_j \right|

    即忽略长度因子。

    显然:

    p2(l,r)p(l,r)p_2(l, r) \le p(l, r)

    因为 summsum|sum| \cdot m \ge |sum|m1m \ge 1

    对于替代罚分函数,总罚分为 subarray sum\sum |\text{subarray sum}|
    由于对整数 x,yx, yx+yx+y|x| + |y| \ge |x + y|,因此最优策略是不拆,直接取整个数组:

    $$\min \sum |\text{subarray sum}| = |a_1 + a_2 + \dots + a_n| $$

    因此原问题的答案 Pmina1+a2++anP_{\min} \ge |a_1 + a_2 + \dots + a_n|


    2. 可达性证明

    我们想证明:存在一种拆分,使 P=a1+a2++anP = |a_1 + a_2 + \dots + a_n|

    第一步:对称性
    若把 aa 中所有元素取反(+--+),每个子数组的绝对值不变,所以答案不变。
    因此可假设 S=ai0S = \sum a_i \ge 0


    第二步:构造

    • S=0S = 0,直接整个数组作为一个子数组,罚分 0n=0|0| \cdot n = 0,达到下界 00

    • S>0S > 0
      寻找最大的 ii,使前缀和 a1++ai=0a_1 + \dots + a_i = 0
      [1,i][1, i] 作为一个子数组,它的和是 00,罚分 00
      下一个元素 ai+1a_{i+1} 必须是 +1+1(否则若它是 1-1,从 00 开始再出现负值,为了最终和 S>0S>0,后面必须出现更大的正和,会使得另一个前缀和再次为 00,这与 ii 最大矛盾)。

      所以 ai+1=1a_{i+1} = 1
      我们将 [i+1,i+1][i+1, i+1] 作为一个长度为 11 的子数组,其和为 11,罚分 11=11 \cdot 1 = 1

      剩余部分 [i+2,n][i+2, n] 的总和变为 S1S - 1

    重复上述过程:每次切出一个 00 和的子数组(可能空)和一个 +1+1 单元素子数组,总罚分增加 11,剩余总和减少 11,直到总和减到 00

    最终总罚分 =S=S= S = |S|


    3. 总结公式

    因此,最小罚分就是整个数组的总和的绝对值:

    ans=i=1nai\text{ans} = \left| \sum_{i=1}^n a_i \right|

    其中 ai=1a_i = 1 对应 '+'ai=1a_i = -1 对应 '-'


    代码实现

    #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;
    }
    

    复杂度分析

    • 时间复杂度:O(n)O(n) 对每个测试用例。
    • 空间复杂度:O(1)O(1) 除了存储字符串。

    验证样例

    1. "+"a=[1]a = [1],总和 11,答案 11
    2. "-----"a=[1,1,1,1,1]a = [-1,-1,-1,-1,-1],总和 5-5,绝对值 55
    3. "+-+-+-"a=[1,1,1,1,1,1]a = [1,-1,1,-1,1,-1],总和 00,答案 00
    4. "--+++++++-"a=[1,1,1,1,1,1,1,1,1,1]a = [-1,-1,1,1,1,1,1,1,1,-1],总和 (11)+(7×1)1=4(-1-1)+(7 \times 1) -1 = 4,答案 44
    5. "+---++++-+++++---++-":计算可得总和绝对值 44
    • 1

    信息

    ID
    6437
    时间
    1000ms
    内存
    256MiB
    难度
    1
    标签
    递交数
    2
    已通过
    1
    上传者