1. 程式人生 > >演算法題-和為S的數字

演算法題-和為S的數字

和為S的數字

題目描述

給定正整數n,求所有和為n的連續序列,如n = 100,則9,10,11,12,13,14,15,16是他的一個連續序列。(劍指Offer 57題)

解題思路

1.利用序列為連續的特性求解

由於這個序列是連續的那麼設這個序列為(a1,a2,…an-1,an),其和Sn的值為k,那麼當k < n時為了增大k得到n,在序列中加入an+1,反之當 k > n時取出當前序列中最小的元素a1,當n == k時則為一個符合要求的解。為了尋找下一個解則繼續在序列後加入下一個數。

由於和為n且序列為連續序列,當n為偶數時序列結尾肯定小於等於n/2,因為(n / 2) + (n / 2) + 1 > n

,當n為奇數時(n / 2) * 2 + 1 == n 所以結尾肯定小於等於(n / 2) + 1 綜上序列搜尋的結尾為Math.ceil(n/2)

綜上所述,我們可以從(1,2)序列開始往後搜尋直到結尾數為Math.ceil(n/2)

程式碼如下

    /**
     * 
     * @param sum 序列和
     * @return 所有符合要求的序列的集合
     */
    public static List<List<Integer>> solve(int sum){
        if(sum < 1)
            throw
new IllegalArgumentException("sum must greater than zero"); List<List<Integer>> result = new ArrayList<>(); if(sum < 3)//和小於3時不存在符合序列 return result; //從1,2序列開始遍歷所有可能 //end最大為sum一半的上取整,因為(sum / 2) + (sum / 2) == sum //sum為偶數則(sum / 2) + (sum / 2) + 1 > sum
//sum為奇數時2 * (sum / 2 + 1) > sum //now用來記錄當前序列和防止每次重複計算 int beg = 1, end = 2, now = 3, half = (int) Math.ceil((double)sum/2); while(end <= half && beg < end){ if(now == sum){ List<Integer> list = new ArrayList<>(); for(int i = beg;i <= end;i++) list.add(i); result.add(list); /* * 找到後末尾新增下一個元素繼續搜尋 * 設新增前首元素為a1,末尾為an-1,和為n * 那麼n + an - a1 > n,所以beg+=2 */ end++;beg+=2; now += end - (2 * beg -3); }else if(now < sum){//序列過小末尾增加下一個 end++; now += end;//計算當前總和值 } else{//序列過大前面減少一個 now -= beg; beg++; } } return result; }

2.利用數學公式求解

設這個序列為(a1,a2,…an-1,an),那麼其和有公式如下

Sn=n(a1+an)2
由於是連續序列所以n = (an - a1 + 1),同時設Sn就是我們需要的結果sum則:
2sum=(ana1+1)(an+a1)
設2 * sum = s * d ( s <= d)
{ana1+1=san+a1=d
因為 s < d 且 s * d = 2 * sum ,所以s,d最相近的值為s = d = Math.sqrt(2 * sum)從這個數開始搜尋直到s == 2這時有d = (2 * sum) / s)
限制條件:由於(a1) + an)和(an) - a1)一定同奇偶,所以(a1) + an)和(an) - a1 + 1)一定一個是奇數一個是偶數,即s和d一定一奇一偶,另外s 和 d一定都是整數。

綜上可寫程式碼如下

public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<>();
        if(sum < 3)
            return result;
        //求s的起始
        int s = (int)Math.sqrt(sum * 2);
        for(int i = s;i >= 2;i--){
            //s,d一定是整數
            if((2 * sum) % i == 0){
                int d = (2 * sum) / i;
                //必須是一奇一偶
                if(((d & 1) == 0 && (i & 1) != 0) || ((d & 1) != 0 && (i & 1) == 0)){
                    int start = (d - i + 1) / 2;
                    int end = (d + i - 1) / 2;
                    ArrayList<Integer> ans = new ArrayList<>();
                    for(int j = start; j <= end;j++){
                        ans.add(j);
                    }
                    result.add(ans);
                }
            }

        }
        return result;
    }