算法题:截断数组(枚举+前缀和)

问题描述

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

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值