题目
给定一个长度为 n 的数组 a1,a2,……,an。
现在,要将该数组从中间截断,得到三个非空子数组。
要求,三个子数组内各元素之和都相等。
请问,共有多少种不同的截断方法?
输入格式
第一行包含整数 n。
第二行包含 n个整数 a1,a2,…,an。
输出格式
输出一个整数,表示截断方法数量。
数据范围
前六个测试点满足 1≤n≤10。
所有测试点满足 1≤n≤10的5次方,−10000≤ai≤10000
输入样例1:
4
1 2 3 3
输出样例1:
1
输入样例2:
5
1 2 3 4 5
输出样例2:
0
输入样例3:
2
0 0
输出样例3:
0
解题思路
第一想法
第一次的思路是直接使用暴力法,能解决一些短的输入数据,但是题目里给的范围是n<10的5次方
大概的时间复杂度是O(nlogn),下面这个方法时间复杂度为O(n²)
#include <iostream>
using namespace std;
const int N=1e5+10;
int a[N],s[N];
int main()
{
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n-1;j++)
{
int a1=0,a2=0,a3=0;
for(int t=i+1;t<=j;t++)
{
a2+=a[t];
}
for(int t=0;t<=i;t++)
{
a1+=a[t];
}
for(int t=j+1;t<=n-1;t++)
{
a3+=a[t];
}
if(a1==a2&&a2==a3)
{
sum++;
}
}
}
cout<<sum<<endl;
}
学习后的想法
看过其它做法之后感觉收获比较大,发现自己的想法还是太少了,其它大佬的做法是先通过求出数组总和的数组
for(int i=1;i<=n;i++)
{
s[i]=s[i-1]+a[i];
}//用s数组来储存a数组i以前的每一个元素的总和
,直接找到符合第二刀的所有下标,然后将所有满足第二刀的情况下(s[i]==s[n]/3*2)的可满足第一刀(下标显示s[i]==s[n]/3)的情况通过枚举法找出来
for(int i=2;i<n;i++)
{
if(s[i-1]==s[n]/3)
{
m++;//将第一刀的数据记录下来
}
if(s[i]==s[n]/3*2)
{
sum+=m;//找到第二刀的情况下
}
}
//最后输出sum
即可计算出所有情况
总代码
#include <iostream>
using namespace std;
const int N=1e5+10;
int a[N],s[N];
int main()
{
long long int n=0,sum=0,m=0;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
s[i]=s[i-1]+a[i];
}
//直接记录总和数组,方便后期计算
if(n<3||s[n]%3!=0)
{
cout<<0<<endl;
return 0;
}
//直接排除特殊值
else
{
for(int i=2;i<n;i++)
{
if(s[i-1]==s[n]/3)
{
m++;
}
if(s[i]==s[n]/3*2)
{
sum+=m;
}
}
cout<<sum<<endl;
}
}