和为S的连续正数序列

  • 题目描述
  • 题目分析
    • 双指针/滑动窗口法
      • C++代码
      • python代码
    • 纯数学技巧法

  数学的基础在算法中的用处大概就是对数字的敏感性了,如果能找到数字的规律,在求解数字的时候往往就能根据规律快速求解。而普通的程序思维相比数学思维,一般就略微显得暴力。

题目描述

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

题目分析

  这个题目也算得上是给你一个数计算机能玩一天的题目,我们仍然从直观的思维开始一步步深入分析。
  要求这个序列,我们可以逐个数字去找从iii开始的序列,显然i>0i>0i>0,但是我们也没有必要所有序列都去找,毕竟以iii开始的序列,最多只有一个等于S,如果我们发现以iii开始,以jjj结尾的序列和大于等于S了,显然后面就不用找了。此外,序列最少要包括两个数,显然如果i≥S2i≥\frac{S}{2}i≥2S​,那么两个数之和肯定大于S了,此时我们也不用找了。我们求和的时候每一次序列多一个数,所以求和的复杂度是O(1)O(1)O(1),但是我们要找多少个序列,这个问题的复杂度就是多少,大致是O(SlgS)O(SlgS)O(SlgS)的。

双指针/滑动窗口法

  我们要表示一个连续序列,我们只需要知道两个数的位置就够了,只需要知道从开始开始,到哪里结束。既然使用双指针的思想,那就可以参考和为S的两个数字,双指针解法。我们使用两个指针,一个记录序列的开始,一个记录序列的结束。
  如果两个指针记录的序列和小于S,则说明应该增大和,需要往序列里面添加数字,此时记录结尾的指针就需要后移,纳入更多的数字。如果序列和大于S,那么说明以序列开头的数字不可能存在和为S的序列了。此时需要减少数字,则开头指针后移。如果和等于S,则把这个序列记录,然后开头指针后移。这样的操作不会漏掉满足需求的值原理和引用的文章类似,有兴趣可以回去查看。
  我们来看一个例子,以S=9为例。初始化前指针pBeg=1,后指针pEnd=2。

pBeg pEnd sum 状态 S 下一步操作
1 2 3小于9 pEnd++
1 3 6小于9 pEnd++
1 4 10大于9 pBeg++
2 4 9等于9(保存) pBeg++
3 4 7小于9 pEnd++
3 5 12大于9 pEnd++
4 5 9等于9(保存) pBeg++,pBeg==5退出

  熟悉了这个过程,差不多可以写出代码了。

C++代码
class Solution {public:vector<vector<int>>  FindContinuousSequence(int S) {vector<vector<int>> res;int pBeg = 1, pEnd = 2,sum=3,mid=S>>1;while (pBeg <= mid){ if(sum>=S){if (sum == S){vector<int> tmp;for (int i = pBeg; i <= pEnd; i++)tmp.push_back(i);res.push_back(tmp);}sum -= pBeg;pBeg += 1;}else{pEnd += 1;sum += pEnd;}}return res;}
};
python代码
class Solution:def FindContinuousSequence(self, S):pBeg = 1pEnd = 2middle = (S + 1)>>1curSum = 3output = []while pBeg < middle:if curSum >= S:if curSum == S:output.append(range(pBeg, pEnd + 1))curSum -= pBegpBeg += 1else:pEnd += 1curSum += pEndreturn output

  这里用的还是双指针或者叫滑动窗口的技巧求解,因为两个指针都是单向前进,没有回溯的过程,所以这个时间复杂度很好分析,就是O(S)O(S)O(S)了,这个技巧还是蛮重要的,可以看到相比第一种分析复杂度已经进步了不少,因为这里已经利用了和的比较关系,省去了很多计算。

纯数学技巧法

  如果我们觉得上面的方法还是在找连续序列的和,这个方法就可以说是直接计算了。我们都知道如果让我们去求连续序列的和,我们肯定会知道一个公式,(首项+尾项)*项数/2。但是这里需要怎么用上这个公式呢。
  我们假设序列的首项是i,尾项是j。我们可以轻易求出sum=(i+j)(j−i+1)/2sum=(i+j)(j-i+1)/2sum=(i+j)(j−i+1)/2,我们把2移到等式左边可以得到2sum=(i+j)(j−i+1)2sum=(i+j)(j-i+1)2sum=(i+j)(j−i+1),显然我们有点二年级的数学水平就知道(i+j)和(j−i+1)(i+j)和(j-i+1)(i+j)和(j−i+1)一个是奇数,一个是偶数。那现在就容易多了,我们直接求出sum的所有奇数因子,那么显然所有的奇数因子就构成了一组或两组解。可以通过质因数分解,把所有的质因数任意组合则得到所有的质数因子。
  为什么是一组或者两组,因为可能i解出来是个负数。我们不妨设这个奇数因子是odd,那么显然那个偶数因子就是sum∗2/oddsum*2/oddsum∗2/odd,这个时候用两个数列两个方程:
{odd=i+j2sumodd=j−i+1and{odd=i+j2sumodd=j−i+1\left \{ \begin{aligned} odd & = & i+j \\ \frac{2sum}{odd} & = & j-i+1 \\ \end{aligned} \qquad and \qquad \right. \left \{ \begin{aligned} odd & = & i+j \\ \frac{2sum}{odd} & = & j-i+1 \\ \end{aligned} \right. ⎩⎨⎧​oddodd2sum​​==​i+jj−i+1​and⎩⎨⎧​oddodd2sum​​==​i+jj−i+1​
  通过这两个方程求解出iii和jjj,然后舍去不符合条件的数值。如果这是一道选择题,到这里也就结束了。我下面给出一个参考代码。只知道在大数测试上,双指针根本无法等待出结果,这个方法很快就能算出来。因为需要质因数分解,难以评估复杂度,代码中需要保存下来的质数可以用于后续计算,这样计算的次数越多,省下来的时间就越多。

class Solution:prim = [2, 3, 5, 7, 11]def primeFactorization(self, tsum):for i in range(self.prim[-1]+1,int(tsum**0.5),2):flag=Truefor j in self.prim:if i%j==0:flag=Falsebreakif flag:self.prim.append(i)i=0fac=[]while i<len(self.prim):if tsum%self.prim[i]:i+=1continuefac.append(self.prim[i])tsum/=self.prim[i]return facdef FindContinuousSequence(self, tsum):fac=self.primeFactorization(tsum)odd=[i for i in fac if i%2]fact=set()res=[]mul=lambda x,y:x*yfor i in range(len(odd)):l=permutations(odd,i+1)for j in l:a=reduce(mul,j)fact.add(a)fact.add((tsum<<1)//a)fact=sorted(list(fact))for i in fact:j=tsum*2//iif i<j+1:continueres.append(list(range((i-j+1)>>1,(i+j+1)>>1)))return res

  这种方法仅供参考,有利于思维方式的提高。总之一句话,数学能力在算法分析中会起到至关重要的作用,之中能力是需要慢慢累积出来的。

和为S的连续正数序列(数学技巧,事半功倍)相关推荐

  1. 和为s的连续正数序列java_剑指Offer41:和为S的连续正数序列(Java)

    思路分析: 在左端建一个窗口[L,R],从左滑倒右.当[L,R]之间的数加起来等于sum,保存起来:若小于sum,则R向右移动:若大于sum,则L向右移动. 题目描述: 小明很喜欢数学,有一天他在做数 ...

  2. 剑指offer:和为S的连续正数序列

    题目描述 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100.但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数).没多久,他 ...

  3. 九度oj 题目1354:和为S的连续正数序列

    题目描述: 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100.但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数).没多久, ...

  4. 《剑指offer》和为s的连续正数序列

    题目:小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100.但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数).没多久,他就得 ...

  5. 剑指Offer之和为S的连续正数序列

    题目描述 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100.但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数).没多久,他 ...

  6. 剑指offe 和为S的连续正数序列

    1.题目 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100.但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数).没多久,他 ...

  7. 【剑指offer{40-44}】和为S的连续正数序列、和为S的两个数字、左旋转字符串、翻转单词顺序列、扑克牌顺子

    文章目录 和为S的连续正数序列 题目描述 C++代码 和为S的两个数字 题目描述 C++代码 左旋转字符串 题目描述 C++代码 翻转单词顺序列 题目描述 C++代码 扑克牌顺子 题目描述 C++代码 ...

  8. 41.和为s的两个数字 VS 和为s的连续正数序列

    为什么80%的码农都做不了架构师?>>>    题目一:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s.如果有多对数字的和等于s,输出任意一对即可.   ...

  9. 和为s的两个数字与和为s的连续正数序列

    题目一:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s.如果有多对数字的和等于s,则输出任意一对即可. 分析:选择数组的两端,将其相加,然后与s比较,如果比s大,则将指向 ...

最新文章

  1. 1069 The Black Hole of Numbers
  2. django 模板里面for循环常用的方法
  3. Operation not allowed after ResultSet closed--操作mysql数据库
  4. python列表get方法_python的get set方法示例
  5. 3·15,你“信”了吗
  6. Popular Cows POJ - 2186(tarjan算法)+详解
  7. CPU 被挖矿,Redis 竟是内鬼!
  8. Repeater嵌套Repeater获取父级绑定项
  9. Single sign-on,什么是单点登陆?
  10. Java中 intValue,parseInt,Valueof 这三个关键字的区别
  11. 狗汪汪玩转嵌入式——I2C 协议分析
  12. 【营销学堂】从饥饿营销到口碑营销
  13. 非常详细的范式讲解(1NF/2NF/3NF/BCNF)
  14. 12306火车票查询--python
  15. mysql spatial 函数_MySQL中spatial基本操作
  16. 计算机组成原理复习大纲
  17. java路上偶遇占小狼
  18. jdk api 1.8 -中文版
  19. 机器学习分类模型评价指标详述
  20. linux系统发qq邮箱文件,Linux打印文件和发送邮件

热门文章

  1. 学习5g通信心得体会_5G学习笔记 - 运营商·运营人 - 通信人家园 - Powered by C114...
  2. 又是一场雨湿了多少人的心
  3. 微信小程序快速提升访问量方法
  4. 基于KNN(K邻近算法)的用电负荷预测(Python代码实现)
  5. NBT封面:水稻NRT1.1B基因调控根系微生物组参与氮利用(作者解读)
  6. 一个好的界面设计应该注意的 75 个原则
  7. AcWing 845. 八数码
  8. flashfxp使用方法,偷偷告诉你flashfxp使用方法
  9. 快手小风车跳转微信生成超链接
  10. 获取xml里的某个属性的值,并把值写入到文件中