问题描述
给定一个长度为 n 的数组 a1,a2,…,an。
现在,要将该数组从中间截断,得到三个非空子数组。
要求,三个子数组内各元素之和都相等。
请问,共有多少种不同的截断方法?
输入格式 输出格式
第一行包含整数 n。 输出一个整数,表示截断方法数量。
第二行包含 n个整数 a1,a2,…,an。
数据范围
前六个测试点满足 1≤n≤10。
所有测试点满足 1≤n≤105,−10000≤ai≤10000。
输入样例1: 输出样例1:
4 1 1 2 3 3
输入样例2: 输出样例2:
5 0
1 2 3 4 5
输入样例3: 输出样例3:
2 0
0 0
思路分析
由题目所给条件“将该数组从中间截断,得到三个非空子数组”和“三个子数组内各元素之和都相等”,可以得出以下结论:
当整数n小于3时,截断方法数量为0
当数组内各元素之和不是3的倍数时,截断方法数量为零。
每个子数组的各元素之和为原数组各元素之和的
设sum为原数组的各元素之和,则均值aver = sum / 3;设total为数组前i项之和,其中i < n。由于数组中至少存在正整数、负整数和零,所以满足total = aver或者满足total = 2*aver的i的取值可能不止一种。
解题的基本依据就是:先确定一个断点(断点满足aver=total),然后枚举出另一个断点的所有位置(断点满足total = 2*aver)。假设断点的数量为p,第j个断点对应的断点的数量为(1<=j <n),则答案为:
由于代码中数组是从前往后遍历的,所以我们将先枚举断点的位置并记录断点出现的次数,如果枚举的过程中碰到断点(即满足total = 2*aver),则当前断点出现的次数就是该断点的对应断点的数量(因为断点出现的位置在该断点的对应所有断点之后,换种说法就是断点的对应所有断点出现在断点前面)。
代码实现
C++代码:
#include<iostream>
using namespace std;
#define MAX 100005
int num[MAX];//原数组
int main(){
long n,sum,i;
while(cin >> n){//读取数据直至文件末尾
sum = 0;
for(i = 1;i <= n;++i){
cin >> num[i];
sum += num[i];
}
if(n < 3 or sum % 3){//判断原数组元素个数以及原数组内各元素之和是否满足存在截断方法的条件
cout << 0 << '\n';
continue;
}
long aver = sum / 3;
sum = 0;
long ans = 0, res = 0;//res为a2个数
for(i = 1; i < n; ++i){
sum += num[i];
if(sum == aver * 2) ans+=res;//判断断点a1
if(sum == aver) res++;//判断断点a2
}
cout << ans << '\n';
}
return 0;
}