演算法題-和為S的數字
阿新 • • 發佈:2019-01-08
和為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 / 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),那麼其和有公式如下
由於是連續序列所以n = (an - a1 + 1),同時設Sn就是我們需要的結果sum則:
設2 * sum = s * d ( s <= 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;
}