1. 程式人生 > >程式設計之美--只考加法的面試題(尺取法)

程式設計之美--只考加法的面試題(尺取法)

問題: 對於一個任意的自然數,問是否能將其拆分成2個或2個以上的連續自然數之和,寫出所有的等式。

解題思路: 第一種解法是推匯出數學公式,因為連續的自然數可以用等差數列Sn求和公式,判斷可行性。公式推導以及證明過程:數學解法
第二種解法是直接窮舉解法,不過對於較大的數字複雜度O(n^2)可能不夠解決,由於連續的自然數一定是遞增狀態,我們可以用尺取法,也就是雙指標法將複雜度降低到O(n)

雙指標解法:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

void print_Answer (ll l, ll r) {
    printf("AC Answer is : ");
    for (ll i = l; i<= r; i++) {
        printf("%d%c", i, " \n"[i==r]);
    }
}

//寫法1
void get_Answer (ll goal) {
    ll i, j, tmp, flag;
    i = 1, j = 1;
    flag = 0;
    //搜尋首項i和尾項j
    while (i <= goal/2 && j <= goal) {
        tmp = (i+j)*(j-i+1)/2;
        if(tmp == goal) {
            print_Answer(i,j);
            flag = 1;
            i++;
            j++;
            continue;
        }
        if(tmp > goal) {
            i++;
            continue;
        }
        j++;
    }
    if(flag == 0) {
        printf("不存在該數的拆分策略!\n");
    }
    return ;
}

//寫法2
void get_Answer2 (ll goal) {
    ll i, j, sum, flag = 0;
    i = j = 1;
    sum = 0;
    while (i <= goal/2) {
        while (j <= goal && sum < goal) {
            sum += j;
            j++;
        }
        if(sum < goal) {
            break;
        }
        if(sum == goal) {
            print_Answer(i,j-1);
            flag = 1;
        }
        sum -= i++; //移位
    }
    if(flag == 0) {
        printf("不存在該數的拆分策略!\n");
    }
    return ;
}

int main() {
    int N;
    scanf("%d", &N);
    printf("寫法1:\n");
    get_Answer(N);
    printf("\n寫法2:\n");
    get_Answer2(N);
    return 0;
}