题解:学而思编程 截断数组
【题目来源】
学而思编程:截断数组
【题目描述】
给定一个长度为 \(n\) 的数组 \(a_1,a_2,...,a_n\)。
现在,要将该数组从中间某处截断,得到两个非空子数组。
要求,第一个子数组内各元素之和等于第二个子数组内各元素之和。
请问,共有多少种不同的截断方法?
【输入】
第一行包含整数 \(n\)。
第二行包含 \(n\) 个整数 \(a_1,a_2,...,a_n\)。
【输出】
输出一个整数,表示截断方法数量。
【输入样例】
3
1 1 1
【输出样例】
0
【核心思想】
-
问题分析:给定长度为 \(n\) 的数组 \(a_1, a_2, \ldots, a_n\),求有多少个截断位置 \(i\)(\(1 \leq i \leq n-1\)),使得前 \(i\) 个元素之和等于后 \(n-i\) 个元素之和。这是一个前缀和 + 线性扫描问题,关键在于利用前缀和快速计算左右两段的和。
-
算法选择:
- 前缀和预处理:
sum[i] = a_1 + a_2 + \ldots + a_i - 枚举截断点:遍历所有可能的截断位置,用前缀和 \(O(1)\) 判断左右和是否相等
- 前缀和预处理:
-
关键步骤:
- 初始化:读取 \(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 == sumr:ans++
- 遍历 \(i\) 从 \(1\) 到 \(n-1\):
- 输出
ans
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),前缀和计算 \(O(n)\),枚举截断点 \(O(n)\)
- 空间复杂度:\(O(n)\),前缀和数组
sum[0..n]
-
前缀和的核心思想:
- 区间求和降维:前缀和
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
浙公网安备 33010602011771号