截断数列
给定一个由 n位数字组成的序列 a1 a2 … an。
其中,每个数字都是 0∼9 之一。
请你判断,能否将数列从中间截断为两个或更多个非空部分,要求每一部分的各位数字之和都相等。
例如,350178 可以截断为 33 个部分 350、17、8,并且满足 3+5+0=1+7=8。
输入格式
第一行包含一个整数 n。
第二行包含 n 个数字 a1,a2,…,an,数字之间不含空格。
输出格式
如果可以按要求截断数列,则输出 YES
,否则输出 NO
。
数据范围
前 66 个测试点满足 2≤n≤10。
所有测试点满足 2≤n≤100,0≤ai≤9。
输入样例1:
5
73452
输出样例1:
YES
输入样例2:
4
1248
输出样例2:
NO
题目来源于:https://www.acwing.com/problem/content/4304/
分析:
这个题的数据范围非常小,所以直接枚举即可。该题核心算法:前缀和
这里先简单介绍一下前缀和:
以原数组a[n],前缀和数组s[n]为例:
- 前缀和:s[i] = a[i]+a[i-1]。
- (l,r)区间和:s[l] = s[l] - s[r-1].
- a[n]数组下标从1开始。因为s[i] = a[i]+a[i-1] = a[i]。
如果对前缀和还不是很理解,可以看下这篇文章
c++灵活实现前缀和与差分
上代码
#include<iostream>
using namespace std;
const int N = 110;
int g[N];
int n;
bool flag;//用来判断是否找到一个方案。
string s;//由于没有空格,所以先当成一个字符串输入。
int main()
{
cin>>n>>s;
for(int i=0;i<n;i++) g[i+1] = s[i] - '0';//将字符串的每一个字符转换成int型数字。
for(int i=1;i<=n;i++) g[i] += g[i-1];//求前缀和。
for(int k=2;k<=n;k++)//枚举截断的段数。
{
if(g[n] % k != 0) continue;//找到一个k,可以使得前n项的和可以被其整除。
int t = g[n]/k;//尝试分成k段,t就是每一段的和。
for(int i=1;i<=n;i++)
{
if(g[i] != t) continue;//找到前缀和当中第一个等于t的数字。
int e = g[i];//将第一个等于t的数字取出。
for(int j = i + 1;j<=n;j++)//依次遍历找到下面的每一段等于t的区间
{
if(g[j] - e == t) e = g[j];//找到一个就把e更新到这个区间的尾部,下一次就从这个区间的尾部出发找下一个区间。
}
if(e == g[n]) flag = true;//每次找完如果最后一次更新的区间尾部刚好是g[n],说明找到了一种方案。
}
}
if(flag == true) cout<<"YES";
else cout<<"NO";
return 0;
}