题目描述
给定一个由 n 位数字组成的序列 a1a2…an。
其中,每个数字都是 0∼9 之一。
请你判断,能否将数列从中间截断为两个或更多个非空部分,要求每一部分的各位数字之和都相等。
例如,350178 可以截断为 3 个部分 350、17、8,并且满足 3+5+0=1+7=8。
输入格式
第一行包含一个整数 n。
第二行包含 n 个数字 a1,a2,…,an,数字之间不含空格。
输出格式
如果可以按要求截断数列,则输出 YES,否则输出 NO。
数据范围
前 6 个测试点满足 2≤n≤10。
所有测试点满足 2≤n≤100,0≤ai≤9。
样例
输入样例1:
5
73452
输出样例1:
YES
输入样例2:
4
1248
输出样例2:
NO
思路:
(暴力枚举) O(n2)O(n2)
根据枚举段数判断
如果枚举的段数k能整除总和 //条件1 允许才可进行下一步
如果 一个区间内的总和大于平均长度s(sum/k),
说明无解,找不到这样的枚举情况,(每个区间总和相等)
如果 一个区间内的总和小于平均长度s(sum/k),
说明该区间还有的数没加上,继续加上再重新判断区间总和与s
否则的话,s==(一个区间的总和) 此时输出YES即可
注意:这里我们规定一个区间末尾的0在下一个区间出现,这样我们的方案就是唯一的
方法一:
时间复杂度 O(n^2)
参考文献
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110;
int n;
char q[N];
int main()
{
cin>>n>>q;
int sum=0;
for (int i = 0; i < n; i ++ )
{
q[i]-='0';
sum+=q[i];
}
for(int k=2;k<=n;k++)
{
if(sum%k==0)
{
bool flag=true;
int s=sum/k;
for (int j = 0,t=0; j < n; j ++ )
{
t+=q[j];
if(t>s)
{
flag=false;
break;
}
else if(t==s)
{
t=0;
}
}
if(flag)
{
puts("YES");
return 0;
}
}
}
puts("NO");
return 0;
}
java代码
import java.io.*;
import java.util.*;
public class Main {
static int n;
public static void main(String args[]) {
Scanner cin = new Scanner(System.in);
int n=cin.nextInt();
String ss=cin.next().strip();
int q[]=new int [n];
int sum=0;
for(int i=0;i<n;i++)
{
char x=ss.charAt(i);
q[i]=x-'0';
sum+=q[i];
}
for(int k=2;k<=n;k++)
{
if(sum%k==0)
{
boolean flag=true;
int s=sum/k;
for (int j = 0,t=0; j < n; j ++ )
{
t+=q[j];
if(t>s)
{
flag=false;
break;
}
else if(t==s)
{
t=0;
}
}
if(flag==true)
{
System.out.println("YES");
return;
}
}
}
System.out.println("NO");
return ;
}
}
其他人的解题方法(大佬的):
方法二:哈希+枚举+前缀和
#include <iostream>
#include <algorithm>
#include <unordered_set>
#include <vector>
#include <string>
using namespace std;
int n;
string s;
vector<int> sm;//前缀和数组
unordered_set<int> st;//标记
int main()
{
cin >> n >> s;
sm = vector<int> (n);
sm[0] = s[0] - '0';
st.insert(sm[0]);
for(int i = 1; i < n; i ++)
{
sm[i] = sm[i - 1] + s[i] - '0';//求前缀和
st.insert(sm[i]);//作标记
}
if(sm.back() == 0) //特例全为0则直接返回YES
{
cout << "YES";
return 0;
}
for(int i = 1; i < sm.back(); i ++)//暴力枚举每一份可能的值,最小是1,最大肯定不超过数组的总和
{
if(sm.back() % i == 0)//总和 % 一部分 (一定) == 0
{
int temp = sm.back() / i;//分成temp份
bool flag = true;
for(int j = 1; j <= temp; j ++)//如果每一份都有则成立
{
if(!st.count(j * i))
{
flag = false;
break;
}
}
if(flag)
{
cout << "YES";
return 0;
}
}
}
cout << "NO";
return 0;
}
//这样写也没问题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 105;
int n;
char s[N];
int main()
{
scanf("%d", &n);
scanf("%s", s);
for (int l = 1; l < n; l++) { // 尝试每种长度
int sum = 0;
for (int i = 0; i < l; i++) {
sum += s[i] - '0';
}
int tmp = 0;
for (int i = l; i < n; i++) {
tmp += s[i] - '0';
if (tmp > sum) break;
else if (tmp == sum) {
if (i + 1 < n && s[i + 1] == '0') continue;
if (i == n - 1) {
puts("YES");
return 0;
} tmp = 0;
}
}
}
puts("NO");
return 0;
}
Python代码
def main():
n = int(input())
s = input()
nums = [0 for _ in range(n)]
tot_sum = 0
for i in range(n):
nums[i] = int(s[i])
tot_sum += nums[i]
for group_cnt in range(2, n + 1):
if tot_sum % group_cnt == 0:
ok = True
group_sum = tot_sum // group_cnt
cur_sum = 0
for x in nums:
cur_sum += x
if cur_sum == group_sum:
cur_sum = 0
elif cur_sum > group_sum:
ok = False
break
if cur_sum != 0:
ok = False
if ok == True:
print("YES")
return
print("NO")
if __name__ == "__main__":
main()
方法三:加了个条件修饰,分成K个区间,K一定是质数,不是质数没有意义
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
bool isPrime(int x)
{
for (int i = 2; i * i <= x; i ++ )
{
if(x % i == 0) return false;
}
return true;
}
int main()
{
int n;
scanf("%d", &n);
vector<int> nums;
int sum = 0;
for (int i = 0; i < n; i ++ )
{
int x;
scanf("%1d", &x);
nums.push_back(x);
sum += x;
}
bool ok = false;
for (int i = 2; i <= n; i ++ )
{
if(!isPrime(i)) continue;
if(sum % i != 0) continue;
int num = 0;
bool ok2 = true;
for (int j = 0; j < nums.size(); j ++ )
{
num += nums[j];
if(num == sum / i)
{
num = 0;
}else if(num > sum / i)
{
ok2 = false;
break;
}
}
if(ok2)
{
ok = true;
break;
}
}
printf(ok?"YES":"NO");
return 0;
}
方法四:前缀和+二分
通过枚举所有第一个数组右端点的位置,数组和sum = a[l],每次二分搜索是否能在其后找到具有相同和为sum的数组。
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int a[110], n;
string s;
int main()
{
cin >> n >> s;
for (int i = 1; i <= n; i ++){
a[i] = s[i - 1] - '0';
a[i] += a[i - 1];
}
for (int i = 1; i < n; i ++ ) {
int l = i;
int sum = a[l];
while(l < n) {
int id = upper_bound(a + 1, a + n + 1, a[l] + sum) - a - 1;
//找不到break
if(id == n + 1 || a[id] - a[l] != sum) {
break;
} else {
//sum = 0 的特殊情况,不处理会死循环
if(l == id) break;
l = id;
}
}
//如果找到最后一个,说明构造出了一个截断数列
if(l == n) {
cout << "YES";
return 0;
}
}
cout << "NO";
return 0;
}
还有一个方法是DP, 今天太晚了,我睡了,就不记录下来了
欢迎留言点赞
嗷嗷嗷