牛客网剑指offer第41题——和为s的连续正数序列

题目:

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

这道题的解法有很多。其实很多人看到算法题的时候,都会有自己的第一思维方式,这个思维方式是很重要的。

那我的第一思维方式是什么?连续正数序列求和,等差数列求和!!!这导致我直接想从等比数列的求和表达式中得到有用信息。题目说是最少有两个数。那么最多有多少呢?我们知道,这个序列额平均值越小,自然项数越多。但最多是多少呢?因为题目说是整数序列,那么最小的序列就是1+2+3...+n = s;此时可以得到最大的n。即

n(n+1)/2 = sum;因此可以得到:n(n+1) = 2*sum;所以,n2<2*sum;所以,n<=(int)sqrt(2*sum);也就是可以找到n的上界。这样我们只需要对n在[2,(int)sqrt(2*sum)]之间进行遍历即可。

经过上一步,我们缩小了n的遍历范围;现在问一个问题:2,(int)sqrt(2*sum)]之间所有的n都满足求和等于sum吗。显然不可能,只有一些满足某些特性的n才能保证连续序列求和为n。

那么我们应该从哪方面入手呢?

我们先从n入手吧,n无非是分为奇数和偶数的情况。那我们就先从n为奇数和偶数入手吧。

1.n为奇数,当n为奇数的时候。

如果连续奇数个正数和等于sum。此时有一个性质。sum/n等于这串数据的平均数。为何?因为(a1+an)/2*n=sum;(a1+an)/2 = sum/n;(a1+an)/2 就是最中间的那个数。学过高中数学的应该都懂这个,即使没学过,也没关系。(a1+an)/2;要么为整数,要么为小数,小数部分是0.5;而一个小数部分为0.5的小数乘以一个奇数。得到的仍然是小数部分你是0.5的小数,不可能等于sum.因此,当n为奇数的时候,若从a1到an连续n个数之和等于sum,(a1+an)/2必为整数,且为中间数。换言之,若n为奇数,且a1,a2....an之和为sum,则sum/n等于中间那个数。进一步,sum%n == 0(都说了可以除尽,等于中间那个数了)。因此我们将 :n%2 ==1 && sum%n==0 作为n为奇数时,连续奇数个正数和为sum的必要条件。

2.n为偶数。当n为偶数的时候

我们还是来看看sum/n这个数是多少,很明显的一个问题是:当n为偶数的时候,(a1+an)/2是等于序列中间两个数的平均数。那么自然小数部分是0.5。比如序列:1,2,3,4;(1+4)/2=(2+3)/2 = 2.5。也就是说:(a1+an)/2 = sum/n=x(x的小数部分为0.5,x为序列中间两个数的平均数)。sum/n为小数,小数部分是0.5.那么sum%n等于多少呢?我们将sum/n表示为:sum/n = (a+0.5)(其中a表示整数部分)。则sum = n*a+n/2;也就是说sum%n = n/2;换言之,我们可以将n%2 == 0 && sum%n = n/2作为n为偶数时,连续偶数个正数和为sum的必要条件

话不多说,上代码:

 1 class Solution {
 2 public:
 3     vector<vector<int> > FindContinuousSequence(int sum) {
 4         //思路:从数学通项求,找出一定的规律
 5         vector<vector<int> >res;
 6         for(int n  = (int)sqrt(2*sum);n>=2;n--)
 7         {//确定满足条件的n
 8             if(n%2==1 && sum%n==0 ||(sum%n)*2 == n)//n为满足条件的奇数
 9             {
10                 vector<int> son_res;
11                 for(int i = sum/n-(n-1)/2,j=0;j < n;i++,j++)
12                     son_res.push_back(i);
13                 res.push_back(son_res);
14             }
15         }
16         return  res;
17     }
18 };

可见,代码十分的简洁!

总结一下思路:

第一步:从数列的特性入手,缩小项数n的查找范围

第二步:将n分为奇数和偶数时分别考虑

第三步:分别考虑范围内奇数条件下的n和偶数条件下的n有何特点才能保证序列之和等于sum

posted @ 2020-03-16 05:58  少年π  阅读(227)  评论(0编辑  收藏  举报