题目描述
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
输出描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
题目链接:
分析:
移动窗口。
左边界left,右边界right,当前和curSum。
大于sum,当前和减去左边数值,left++。
小于sum,当前和增加右边数值,right++。
结束条件,right最大值是 sum/2+1。
(写法中由于每次右移后+1,所以判断时right<mid +1)
import java.util.ArrayList; public class Solution { public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) { ArrayList<ArrayList<Integer>> list = new ArrayList<>(); if(sum<=1){ return list; } int mid = sum/2+1; int left = 1; int right = 2; //记录当前和 int curSum = 1; //结束条件,left最大是mid-1,right最大是mid,因为每次右移后有right++操作,所有判断right <= mid+1 while(right <= mid + 1){ //小于目标和时,右边界往右移动,并添加到当前和上 if(curSum<sum){ curSum+=right; right++; }else if(curSum>sum){ //当大于目标和时,左边界向右移动,减去最左边的值 curSum-=left; left++; }else{ //当前和与目标和相同时,一个答案 ArrayList<Integer> tmp = new ArrayList<>(); for(int i=left;i<right;i++){ tmp.add(i); } list.add(tmp); //答案记录完后,右边界向右边移动 curSum +=right; right++; } } return list; } }
方法2详见:
https://www.nowcoder.com/profile/645151/codeBookDetail?submissionId=1519890
分析:以 连续正数的数量 i 为突破点。
根据等差数列的求和公式: S = (1 + n) * n / 2,得到.,所以遍历区间:[2 ,2S的开方]
根据连续数量判断是否存在相应的区间,使该区间的和等于目标和sum。
1、连续数量len为 奇数。可知 i * mid = sum ,所以有 sum%i == 0。(mid为连续序列的平均值即中间值)
2、连续数量len为 偶数。 [a+0,a+1,a+2……a+i-1]的和对 i 取余 ,不难看出只需求[0,1,2 ……,i-1 ]的和对 i 取余,结果为 ( i - 1)/2,因为 i 为偶数,所以 i/2 == (i-1)/2, 所以sum % i *2 = i。。 例: a + 0,a + 1,a + 2, a + 3 可知,(4*a + (0+ 3)/2 * 4) % 4 = 2。
知道 i 连续正数的数量存在相应区间,求区间位置。
i * mid = sum,所以mid = sum / i,找到中间值, 减去(i-1)/2找到左边界。所以有 (sum/i) - (i-1)/2
import java.util.ArrayList; public class Solution { public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) { ArrayList<ArrayList<Integer>> list = new ArrayList<>(); for(int i = (int)Math.sqrt(2*sum);i >=2;i--){ if((i&1)==1&&sum%i==0||sum%i*2==i){ ArrayList<Integer> tmp = new ArrayList<>(); for(int j=(sum/i) - (i-1)/2,k=0;k<i;k++,j++){ tmp.add(j); } list.add(tmp); } } return list; } }