题解:学而思编程 截断数组

【题目来源】

学而思编程:截断数组

【题目描述】

给定一个长度为 \(n\) 的数组 \(a_1,a_2,...,a_n\)

现在,要将该数组从中间某处截断,得到两个非空子数组。

要求,第一个子数组内各元素之和等于第二个子数组内各元素之和。

请问,共有多少种不同的截断方法?

【输入】

第一行包含整数 \(n\)

第二行包含 \(n\) 个整数 \(a_1,a_2,...,a_n\)

【输出】

输出一个整数,表示截断方法数量。

【输入样例】

3
1 1 1

【输出样例】

0

【核心思想】

  1. 问题分析:给定长度为 \(n\) 的数组 \(a_1, a_2, \ldots, a_n\),求有多少个截断位置 \(i\)\(1 \leq i \leq n-1\)),使得前 \(i\) 个元素之和等于后 \(n-i\) 个元素之和。这是一个前缀和 + 线性扫描问题,关键在于利用前缀和快速计算左右两段的和。

  2. 算法选择

    • 前缀和预处理sum[i] = a_1 + a_2 + \ldots + a_i
    • 枚举截断点:遍历所有可能的截断位置,用前缀和 \(O(1)\) 判断左右和是否相等
  3. 关键步骤

    • 初始化:读取 \(n\) 和数组 \(a[1..n]\)
    • 计算前缀和
      • sum[0] = 0
      • 遍历 \(i\)\(1\)\(n\)
        • sum[i] = sum[i-1] + a[i]
    • 枚举截断点
      • 遍历 \(i\)\(1\)\(n-1\)
        • suml = sum[i]:前 \(i\) 个元素之和
        • sumr = sum[n] - sum[i]:后 \(n-i\) 个元素之和
        • suml == sumrans++
    • 输出 ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n)\),前缀和计算 \(O(n)\),枚举截断点 \(O(n)\)
    • 空间复杂度:\(O(n)\),前缀和数组 sum[0..n]
  5. 前缀和的核心思想

    • 区间求和降维:前缀和 sum[i] 存储前 \(i\) 项累计和,任意区间 \([l, r]\) 的和可通过 sum[r] - sum[l-1]\(O(1)\) 内计算
    • 截断条件转化:左右两段和相等等价于 sum[i] = sum[n] - sum[i],即 2 * sum[i] = sum[n],也可用于提前判断总和是否为偶数
    • 非空约束保证:截断点 \(i\)\(1\)\(n-1\),确保两段均非空
    • 线性扫描即可:截断点有序,无需复杂数据结构,一次遍历统计满足条件的位置
    • 适用于"数组分割 + 和相等"类问题,前缀和将 \(O(n^2)\) 的暴力枚举优化为线性扫描

【算法标签】

模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, a[100005], sum[100005], ans;
int main()
{
    cin >> n;  // 输入数组长度
    for (int i = 1; i <= n; i++)  // 输入数组元素
    {
        cin >> a[i];
        sum[i] = sum[i - 1] + a[i];  // 计算前缀和
    }

    // 遍历所有分割点i,检查是否可以将数组分成和相等的两部分
    for (int i = 1; i <= n - 1; i++)  // i从1到n-1
    {
        int suml = sum[i];  // 左边部分的和:a[1]到a[i]
        int sumr = sum[n] - sum[i];  // 右边部分的和:a[i+1]到a[n]

        if (suml == sumr)  // 如果左右两部分和相等
        {
            ans++;  // 计数加1
        }
    }

    cout << ans << endl;  // 输出分割点数量
    return 0;
}

【运行结果】

3
1 1 1
0
posted @ 2026-07-04 15:04  团爸讲算法  阅读(9)  评论(0)    收藏  举报