截断函数(控制时间复杂度的枚举法)

题目

给定一个长度为 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;
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值