题目描述
给定一个由 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#include #include 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;is)
{
flag=false;
break;
}
else if(t==s)
{
t=0;
}
}
if(flag==true)
{
System.out.println("YES");
return;
}
}
}
System.out.println("NO");
return ;
}
}
其他人的解题方法(大佬的):
方法二:哈希+枚举+前缀和
#include#include #include #include #include using namespace std; int n; string s; vector sm;//前缀和数组 unordered_set st;//标记 int main() { cin >> n >> s; sm = vector (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 #include #include 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#include #include #include 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 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#include #include 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, 今天太晚了,我睡了,就不记录下来了
欢迎留言点赞
嗷嗷嗷



