朋友们好!今天要和大家分享的是一个有趣的题,这是2016年美国信息学奥赛USACO一月赛季的题目,中文名叫“与7无关的数”,你将在本文中了解到前缀和的知识,并且了解到一道难题是怎么做出来的。(注,这是一个困难的过程)
文章目录
(本文适合学习C++有那么一点点基础,但基础又不是特别高的朋友)
A.题目描述
题目描述
给你n个数,分别是a[1],a[2],…,a[n]。求一个最长的区间[x,y],使得区间中的数(a[x],a[x+1],a[x+2],…,a[y-1],a[y])的和能被7整除。输出区间长度。若没有符合要求的区间,输出0。
输入
第一行一个数n,接下来为n个数,每个数在0~1000000范围内,1<=n<=50000
输出
输出最大区间长度
样例输入
7
3 5 1 6 2 14 10
样例输出
5
提示
来源
USACO2016JAN
B.初步分析
一开始你看到这道题是什么感受呢?你会不会说:这道题不就是道枚举题嘛,so easy!
1.0版方法
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
for(int k=i;k<=j;k++){
s+=a[k];
}
if(s%7==0)
m=max(j-i,m);
s=0;
}
}
可是,注意一下数据范围,三重循环,明晃晃的超时呀!
于是,你是否人有了改进枚举的想法——
1.5版方法
for(int i=1;i<=n;i++){
int s=0;
for(int j=i;j<=n;j++){
s+=a[j];
if(s%7==0)
m=max(j-i,m);
}
}
看上去好些了,那么让我来告诉自信的你,时间超限了……
等等,换种思维试试——
1.7版方法
for(int i=1;i<=n;i++){
int s=0;
for(int j=n;j>=i;j--){
s+=a[j];
if(s%7==0){
m=max(j-i,m);
break;
}
}
}
哈哈,从今以后请记住:不狡猾的出题人是不聪明的。单纯地用枚举,无论怎样都会时间超限……
C.算法改进
这时,总算有人想起前缀和与差分。
前缀和直接就求出了1~i之间的所有数值和,不是很好用吗?
(不懂得前缀和的朋友,请直接把文档拉到底部E.3处。)
2.0版方法
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);//注意,a[i]太大,要用long long定义,也就得用lld输入
b[i]=b[i-1]+a[i];
}
这里不就求出数组a的前缀和数组b了吗,我们继续——
for(int i=0;i<=n;i++){
for(int j=n;j<