给定一个由 n 位数字组成的序列
a
1
a
2
…
a
n
a_1a_2…a_n
a1a2…an。其中,每个数字都是
0
∼
9
0∼9
0∼9之一。
请你判断,能否将数列从中间截断为两个或更多个非空部分,要求每一部分的各位数字之和都相等。
例如,
350178
350178
350178
可以截断为 3 个部分
350
350
350、
17
17
17、
8
8
8,并且满
3
+
5
+
0
=
1
+
7
=
8
3+5+0=1+7=8
3+5+0=1+7=8。
输入格式
第一行包含一个整数
n
n
n。
第二行包含 n
个数字
a
1
,
a
2
,
…
,
a
n
a_1,a_2,…,a_n
a1,a2,…,an,数字之间不含空格。
输出格式
如果可以按要求截断数列,则输出 YES,否则输出 NO。
分析 通过计算序列全部和,枚举2~n段所有可能段,如果存在整除,说明可以分成k段,k段里依次检验每段和是否成立,当前如果枚举x是的子段和>枚举假定和就pass 直到枚举完毕再判断即可
/*
法一参考了AcWing的官方题解
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int a[N];
int cnt[N];
int main()
{
int sum = 0;
int n;
string str;
cin >> n >> str;
for (int i = 0; i < n; ++ i) a[i + 1] = str[i] - '0';
for (int i = 1; i <= n; ++ i) sum += a[i];
for (int k = 2; k <= n; ++ k)
{
if (sum % k == 0)
{
bool flag = true;
int s = sum / k;
for (int j = 1 , t = 0; j <= n; ++ j) // 这里方法很巧妙,递增枚举子段和
{
t += a[j];
if (t > s)
{
flag = false;
break;
}
else if (t == s) t = 0;
}
if (flag)
{
puts("YES");
return 0;
}
}
}
puts("NO");
return 0;
}
法二:我们通过枚举前缀和就表示枚举了第一段的所有可能存在,存下来后我们再依次枚举段数,假设枚举段数为k,如果序列同时存在 k , 2 k , 3 k . . . . . k , 2k , 3k..... k,2k,3k.....则成立否则不成立,前缀和+哈希
两种方法复杂度均为 O ( n 2 ) O(n^2) O(n2)
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
unordered_set<int> s;
int a[N];
int n;
int main()
{
string str;
cin >> n >> str;
for (int i = 0; i < n; ++ i)
{
a[i + 1] = a[i] + str[i] - '0';
s.insert(a[i + 1]);
}
//for (int i = 1; i <= n; ++ i) cout << a[i] << ' ';
for (int k = 2; k <= n; ++ k)
{
bool flag = true;
if (a[n] % k == 0)
{
int temp = a[n] / k;
for (int j = 1; j <= k; ++ j)
{
if (!s.count(j * temp))
{
flag = false;
break;
}
}
if (flag)
{
puts("YES");
return 0;
}
}
}
puts("NO");
return 0;
}