2018.11.7 《剑指Offer》从零单刷个人笔记整理(66题全)目录传送门
这道题也是一道效率题,有两种比较技巧的做法。
方法一:序列规律
题目隐含条件为该数本身不算一个结果序列,那满足要求的序列一共有3种:1.两个数组成的序列,2.多个数组成的序列(奇数个数),3.多个数组成的序列(偶数个数)。
两个数组成的序列:很容易知道满足条件,简单地除2即可得到序列的前一个数。例如51由序列{25,26}加和而成。
多个数组成的序列(奇数个数):奇数个数的中位数必定是S的一个因数,奇数的个数为S的另一个因数,只需要在计算过程中保证序列最小值>0不超限即可。例如51的其中一个因数是17,另一个因数是3,那么以17为中心个数为3的的数列{16,17,18}就是其中一个满足条件的数列。而因数3作为中心值长度为17的序列最小值必然小于0,因此无法构成序列。
多个数组成的序列(偶数个数):偶数个数的序列情况与奇数个数的情况相似,最中心两个数的和为S其中一个因数的2倍。序列的元素个数为S的另一个因数。这里需要注意的是两个因子均为偶数的数S不能构成满足条件的序列。例如18的两个因数分别是9和2,那么{3,4,5,6}是一个满足的序列。
最后只需要按题目要求对满足要求的序列进行排序即可。
方法二:动态规划
从基础序列{1,2}开始,1为small,2为big,sum为3。若sumS,small前进。每当sum==S的时候保存序列并使big前进,当small前进到S/2或者big前进到S/2+1停止。
例如对于S=9来说,初始序列{1,2},sum=3。
3<9,big前进,序列{1,2,3},sum=6;
6<9,big前进,序列{1,2,3,4},sum=10;
10>9,small前进,序列{2,3,4},sum=9,保存序列;
big前进,序列{2,3,4,5},sum=14;
14>9,small前进,序列{3,4,5},sum=12;
12>9,small前进,序列{4,5},sum=9,保存序列;
small=9/2,算法停止。
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序。
Java实现(方法一):
/**
*
* @author ChopinXBP
* 输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序。
*
*/
import java.util.ArrayList;
import java.util.Collections;
public class FindContinuousSequence_39 {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList> result = FindContinuousSequence(3);
for(int i = 0; i < result.size(); i++){
for(int j = 0; j < result.get(i).size(); j++){
System.out.print(result.get(i).get(j) + " ");
}
System.out.println("");
}
}
public static ArrayList> FindContinuousSequence(int sum) {
if(sum <= 0)return null;
ArrayList> result = new ArrayList<>();
if(sum <= 2)return result;
//长度为2个数的序列(只有奇数有)
if((sum & 0x01) == 1){
ArrayList list = new ArrayList<>();
list.add(sum / 2);
list.add(sum / 2 + 1);
result.add(list);
}
//长度大于2个数的序列
ArrayList factor = new ArrayList<>();
for(int i = 1; i < sum; i++){
if(sum % i == 0){
factor.add(i);
}
}
for(int i = 0; i < factor.size(); i++){
int curfactor = factor.get(i);
int otherfactor = sum / curfactor;
//另一个因子为偶数
if((otherfactor & 0x01) == 0){
//两个因子均为偶数不可能构成序列
if((curfactor & 0x01) == 0) continue;
//以和为curfactor的两点为中心的序列长度不会超过正数界
int length = otherfactor / 2 - 1;
int center = curfactor / 2;
if(center > length){
ArrayList list = new ArrayList<>();
for(int j = center - otherfactor + 1; j <= center + otherfactor; j++){
list.add(j);
}
result.add(list);
}
}
//另一个因子为奇数
else{
//以curfactor为中心点的序列长度不会超过正数界
int length = otherfactor / 2;
if(curfactor > length){
ArrayList list = new ArrayList<>();
for(int j = curfactor - length; j <= curfactor + length; j++){
list.add(j);
}
result.add(list);
}
}
}
Collections.sort(result, (ArrayList list1, ArrayList list2) -> Integer.compare(list1.get(0), list2.get(0)));
return result;
}
}
C++实现(方法二):
#include "stdafx.h"
void PrintContinuousSequence(int small, int big);
void FindContinuousSequence(int sum)
{
if(sum < 3)
return;
int small = 1;
int big = 2;
int middle = (1 + sum) / 2;
int curSum = small + big;
while(small < middle)
{
if(curSum == sum)
PrintContinuousSequence(small, big);
while(curSum > sum && small < middle)
{
curSum -= small;
small ++;
if(curSum == sum)
PrintContinuousSequence(small, big);
}
big ++;
curSum += big;
}
}
void PrintContinuousSequence(int small, int big)
{
for(int i = small; i <= big; ++ i)
printf("%d ", i);
printf("\n");
}
测试代码:
// ====================测试代码====================
void Test(char* testName, int sum)
{
if(testName != NULL)
printf("%s for %d begins: \n", testName, sum);
FindContinuousSequence(sum);
}
int _tmain(int argc, _TCHAR* argv[])
{
Test("test1", 1);
Test("test2", 3);
Test("test3", 4);
Test("test4", 9);
Test("test5", 15);
Test("test6", 100);
return 0;
}
#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#