剑指offer_牛客_JZ41——和为S的连续正数序列(新的补充)

题目描述

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

返回值描述:

输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

示例1

输入

9

返回值

[[2,3,4],[4,5]]

思路

方法四:按序列大小遍历

这道题我又想了一下,感觉上一篇文章方法三对a进行遍历还是不够快。因为如果sum为100的话,a要遍历50次。但事实上倒数第一个可能的a肯定小于50,因为两个数相加等于100,那a一定小于sum/2。倒数第二个可能的a肯定小于32,因为三个数相加等于100,那a一定小于sum/3-1。这样的话我只需要遍历到sum/n-(n-1)/2<1的时候就好。通过n+(n*n-n)/2 > sum解出n=14,相比50次要小很多。
在这个基础上,如果我们利用以下三个定理,就可以排除更多的选项。

  1. 当n为偶数时,n个连续的数相加一定不能被n除尽
  2. 当n为偶数时,n个连续的数相加再加n/2一定能被n除尽
  3. 当n为奇数时,n个连续的数相加一定能被n除尽

以100为例,整个遍历过程就是这样的:

1. 100可以被2整除,所以2个连续的数不可能加起来等于100
2. 100不可以被3整除,所以3个连续的数不可能加起来等于100
3. 100可以被4整除,所以4个连续的数不可能加起来等于100
4. 100可以被5整除,所以5个连续的数可能加起来等于100
    a = 100/5-(5-1)/2 = 18; b = a+n-1 = 22
5. 100+6/2不可以被6整除,所以6个连续的数不可能加起来等于100
6. 100不可以被7整除,所以7个连续的数不可能加起来等于100
7. 100+8/2可以被8整除,所以8个连续的数可能加起来等于100
    a = 100/8-(8-1)/2 = 9; b = a+n-1 = 16
8. 100不可以被9整除,所以9个连续的数不可能加起来等于100
9. 100可以被10整除,所以10个连续的数不可能加起来等于100
10. 100不可以被11整除,所以11个连续的数不可能加起来等于100
11. 100+12/2不可以被12整除,所以12个连续的数不可能加起来等于100
12. 100不可以被13整除,所以13个连续的数不可能加起来等于100
13. 100+14/2不可以被14整除,所以14个连续的数不可能加起来等于100

我觉得新的这个方法在数字特别大的时候可以更快地解决这个问题。

题解:

class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        vector<vector<int> > ans;
        for(int n=2;n+(n*n-n)/2<=sum;n+=1){
            int a,b;
            if(n%2==0 && (sum+n/2)%n == 0 || n&1 && sum%n == 0){
                a = sum/n - (n-1)/2;
                b = a+n-1;
                vector<int> temp;
                for(int i=a;i<=b;i++){
                    temp.push_back(i);
                }
                ans.push_back(temp);
            }
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

实际运行效果

我对比了按序列大小遍历、一元二次求根、滑动窗口三个方法,发现按序列大小遍历果然是最快的方法。但是之前认为一元二次求根比滑动窗口快却是错的,目前还没弄清楚为什么,后续会继续探究,欢迎交流。

对照代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
#include <cmath>

using namespace std;

class Solution1 {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        vector<vector<int> > ans;
        for(int n=2;n+(n*n-n)/2<=sum;n+=1){
            int a,b;
            if(n%2==0 && (sum+n/2)%n == 0 || n&1 && sum%n == 0){
                a = sum/n - (n-1)/2;
                b = a+n-1;
                vector<int> temp;
                for(int i=a;i<=b;i++){
                    temp.push_back(i);
                }
                ans.push_back(temp);
            }
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

class Solution2 {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        vector<vector<int> > ans;
        for(int a=1;a<=sum/2;a++){
            double x = -1.0/2 + sqrt(1.0/4+4*(2*sum+a*a-a))/2;
            int b = round(x);
            if(b*b - a*a + a + b == 2*sum){
                vector<int> temp;
                for(int i=a;i<=b;i++){
                    temp.push_back(i);
                }
                ans.push_back(temp);
            }
        }
        return ans;
    }
};

class Solution3 {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        //子序列和的最大值
        int End=sum/2+1;
        int left=1;
        int right=1;
        int CurSum=1;
        vector<vector<int> >res;
        while(left<=End-1&&right<=End)
        {
            if(CurSum==sum)
            {
                GetVector(res,left,right);
                right++;
                CurSum-=left;
                left++;
                CurSum+=right;
            }
            else if(CurSum<sum)//增加右边界
            {
                right++;
                CurSum+=right;
            }
            else
            {
                CurSum-=left;
                left++;
            }
        }
        return res;
    }
    void GetVector(vector<vector<int> >&res,int left,int right)
    {
        if(left==right)
        {
            return ;
        }
        vector<int>V;
        for(int i=left;i<=right;i++)
        {
            V.push_back(i);
        }
        res.push_back(V);
    }
};

int main(){
    Solution1 s1 = Solution1();
    Solution2 s2 = Solution2();
    Solution3 s3 = Solution3();
    vector<vector<int> > res;
    time_t first,second;
    first = time(NULL);
    for (int i=0;i<10000;i++)
        res = s1.FindContinuousSequence(100000);
    second = time(NULL);
    printf("s1(按序列大小遍历): %f seconds\n", difftime(second, first));
    for(int i=0;i<res.size();i++){
        for(int j=0;j<res.size();j++)
            cout<<res[i][j]<<' ';
        cout<<endl;
    }
    first = time(NULL);
    for (int i=0;i<10000;i++)
        res = s2.FindContinuousSequence(100000);
    second = time(NULL);
    printf("s2(一元二次求根): %f seconds\n", difftime(second, first));
    for(int i=0;i<res.size();i++){
        for(int j=0;j<res.size();j++)
            cout<<res[i][j]<<' ';
        cout<<endl;
    }
    first = time(NULL);
    for (int i=0;i<10000;i++)
        res = s3.FindContinuousSequence(100000);
    second = time(NULL);
    printf("s3(滑动窗口): %f seconds\n", difftime(second, first));
    for(int i=0;i<res.size();i++){
        for(int j=0;j<res.size();j++)
            cout<<res[i][j]<<' ';
        cout<<endl;
    }
    
    return 0;
}

对照结果:

s1(按序列大小遍历): 0.000000 seconds
153 154 155 156 157 
738 739 740 741 742 
1531 1532 1533 1534 1535 
3988 3989 3990 3991 3992 
19998 19999 20000 20001 20002 
s2(一元二次求根): 5.000000 seconds
153 154 155 156 157 
738 739 740 741 742 
1531 1532 1533 1534 1535 
3988 3989 3990 3991 3992 
19998 19999 20000 20001 20002 
s3(滑动窗口): 3.000000 seconds
153 154 155 156 157 
738 739 740 741 742 
1531 1532 1533 1534 1535 
3988 3989 3990 3991 3992 
19998 19999 20000 20001 20002 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容