两种枚举方法Acwing3956.截断数组(枚举,前缀和) C++

题目

Acwing3956.截断数组

给定一个长度为n的数组 a1,a2,…,an。

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

要求,三个子数组内各元素之和都相等。

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

输入格式

第一行包含整数 n。

第二行包含 n 个整数 a1,a2,…,an。

输出格式

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

数据范围

前六个测试点满足 1 ≤ n ≤ 10。

所有测试点满足 1 ≤ n ≤ 1 0 5 10^{5} 105 ,−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

思路

题目要求:
1.分成三个非空子数组
2.三个子数组内各元素之和都相等
必然可以通过特判元素总和是否为3的倍数来判断是否合题意。

接下来我们就要考虑如何划分是合理的,有几种划分方式。

枚举第一刀时:

在这里插入图片描述
因为 i 后面还有( i + 1 , j - 1 ) 和 ( j , n ) 两段元素所以最大为 n-2
j 的前面有两段元素,所以最小为3
所以 j = i + 2。

当第一段( 1 , i ) 的元素值总和 为 所有元素值总和的三分之一时 第一刀划分符合题意,
然而我们还需要判断第二刀划分后 ( j , n ) 这段的数总和是否也等于总和的三分之一,那么最方便的就是前缀和预处理

for(int i=1;i<=n;i++){
    cin >> s[i];
    s[i]=s[i-1]+s[i];
}

前缀和中求 i 到 j 范围的数总和为s [ j ] - s [ i - 1 ]
那么求 ( j , n ) 这段的数总和则为s [ n ] - s [ j - 1 ] 也就是 s [ n ] - s [ i + 1 ]

for(int i = 1 ;i <= n-2; i++){
	if(s[i] == average) cnt++;
	if(s[n]-s[i+1] == average)	ans += cnt;
}

枚举第二刀时:

在这里插入图片描述
同理可得i和j的取值范围
易得 ( i , n ) 这段的数总和为s [ n ] - s [ i - 1 ]

for(int i = 3 ;i <= n; i++){
	if(s[i-2] == average) cnt++;
	if(s[n]-s[i-1] == average)	ans += cnt;
}

完整代码

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N = 1e5+10;
int n; 
LL s[N];
int main(){
    cin >> n;
    for(int i = 1;i <= n;i++){
		cin >> s[i];
		s[i] += s[i-1];
    }//创造前缀和数组,因为最大10^5*10^5>2*10^9,所以不可以用int存
    if(s[n] % 3 != 0 || n < 3){//所有和不是三的倍数或者是个数小于三输出0 
        cout << "0" << endl;
        return 0;
    }
    int average = s[n] / 3,cnt = 0;
    LL ans = 0;//可能超范围 
    
	for(int i = 1 ;i <= n-2; i++){//判断第一刀时
		if(s[i] == average) cnt++;
		if(s[n] - s[i+1] == average)	ans += cnt;
	}
	/*
	for(int i = 3 ;i <= n; i++){//判断第二刀时
		if(s[i-2] == average) cnt++;
		if(s[n] - s[i-1] == average)	ans += cnt;
	}
	*/
    cout << ans << endl;
    return 0;
}

如有不对,欢迎指正

都看到这了,不点个赞嘛 O 。0 ?

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值