演算法問題分類---整數和問題系列總結

來源:互聯網
上載者:User

標籤:演算法   子數組和   子集和   最大和   

1.給定正整數N,列出所有總和為N的連續正整數串。

方法1:

可以用兩個數small,big分別表示序列的最小值和最大值。首先把small初始化為1,big初始化為2。如果從small到big的序列的和大於n的話,則右移small即從序列中移除最小的數;如果從small到big的序列的和小於n的話,則右移big,相當於向序列中添加下一個數字,一直到small等於(1+n)/2,保證序列中至少有兩個數。

方法2:

設最大長度為x(加起來等於同樣值的元素最多,則這些元素從最小的元素裡取),則

1+2+...+ x = N  => x(x+1)/2 = N

=>x = sqrt(2N+1/4)-1/2 <= sqrt(2N)

設待求出子序列的長度為len ,則依次從len = x-->1找子序列就行了

設序列開始的位置的元素值為s,則對於長度為len的子序為

s,s+1,....s + x-1

該子序列的和為x*s + x(x-1)/2 = N     => s = N/x - (x-1)/2

若s為整數,則s,s+1,.... s + x-1即為所求的合格子序列。

代碼(來自網上):

#include <stdio.h>#include <math.h>int main(){   int m=0,  n=0,  start=0,  end=0,  flag=0;   //m為輸入的數,n為正整數序列個數,start為顯示的開始正整數,end為顯示的結束正整數, //flag表示有無合格序列存在,0(預設)沒有,1有   float temp=0.0;   printf("please input a Integer:");   scanf("%d",&m);  printf("\n");  n=(int) (sqrt((double)m)*sqrt((double)2)); //求出可能的最大的序列個數 while(n>=2) {   temp=(float)m/n-(float)(n-1)/2;  //求開始數   if(temp==(int) temp)  {     //判斷是不是正整數,即有沒有符合合格序列存在    for(flag=1,start=(int) temp,end=start+n;start<end;start++)   {          //flag標誌置1,有符合合格序列存在,得出開始整數start和結束整數end      printf("%d",start);      printf(" ");    }     printf("\n");    }    n--;   } if(flag==0)       //沒有符合合格序列存在,則輸出"none"    printf("none");}


2.和為S的兩個數

輸入一個遞增排序的數組和一個數字S,在數組中尋找兩個數,是的他們的和正好是S,如果有多對數位和等於S,輸出兩個數的乘積最小的。

思想:因為已經排好序,那麼如果存在那麼兩個數,可能是一個比較大,一個比較小,或者是比較相等的兩個數!所以我們設一個n1=min(即a[0]),n2=max(即a[n-1] )開始,實際是n1 = a[low],low=0開始,n2 = a[high],high=n-1開始,然後n1+n2與S(我們需要的和)進行比較,若>,則n2=a[--high];若<, n1=a[++low],若==,則使用mul儲存乘積,還要儲存兩個數,因為可能不只一對數,它只要乘積小的!掃描直到low>=high終止!

 

3.判斷一包含n個整數a[]中是否存在i、j、k滿足a[i] + a[j] = a[k]的時間複雜度最小值是:

A.O(n^2)        B. O(n^2*logn)       C. O(n^3)   D. O(nlogn)

分析:先遞增排序(O(nlgn)),再固定每一個元素,按照求和為S的兩個數字S的方法求解(n*O(n)=O(n^2))

求和問題總結(leetcode 2Sum, 3Sum, 4Sum, K Sum)

http://blog.csdn.net/doc_sgl/article/details/12462151

曾經在網上見了一個文章討論該問題O(nlgn)的方法,可惜現在一直找不到了,大概是利用相反數之類的思想。

 

4.輸入兩個整數 n 和 m,從數列1,2,3.......n 中隨意取幾個數,使其和等於 m ,要求將其中所有的可能組合列出來。(0-1背包問題)

分析:

問題其實本質上就是0/1背包問題,對於每一個n,我們採用貪婪策略,先考察是否取n,如果取n,那麼子問題就變成了find(n-1,m-n),而如果捨棄n,子問題則為find(n-1,m)。

那麼,如何制定解的判定策略?我們知道,遞迴需要邊界條件,而針對背包問題,邊界條件只有兩種,如果n<1或者m<1,那麼便相當於“溢出”,無法combo出m,而另一種可能就是在剩餘的n個裡恰好滿足m==n,即此時背包剛好填充滿,輸出一組解單元。除此之外,再無其他。為了減小遞迴深度,當n>m時,因為和等於m的數必小於等於m,所以可以設定n=m.

演算法:

1.首先判斷,如果n>m,則n中大於m的數不可能參與組合,此時置n=m;

2.將最大的數n加入且n==m,則滿足條件,輸出;

3.將n分兩種情況求解:n沒有加入,取n=n-1,m=m,遞迴;

4.n加入,取n=n-1,m=m-n,遞迴。

5.結束。

代碼(來自網上):

intlength;/*輸入兩個整數n 和m,從數列,,.......n 中隨意取幾個數, 使其和等於m ,要求將其中所有的可能..*/voidfindCombination(int n,intm,bool *flag){  if(n < 1 || m < 1)   return;  if(n > m)//--減少遞迴次數----   n = m;  if(n == m) {   flag[n-1] = 1;   for(inti=0;i<length;i++)   {     if(flag[i] == true)       printf("%d\t",i+1);   }   printf("\n");   flag[n-1] = 0; } flag[n-1] = 1; findCombination(n-1,m-n,flag); flag[n-1] = 0; findCombination(n-1,m,flag);} intmain(){  int n, m; printf("n  sum\n"); scanf("%d%d",&n,&m); length = n;  bool *flag = (bool*)malloc(sizeof(bool)*length); findCombination(n,m,flag); free(flag);  return 0;}


5.子集和問題(回溯法狀態空間樹)---問題4的推廣

求n個正整數構成的一個給定集合S={s1,s2,…sn}的子集,子集的和要等於一個給定的正整數d。例如對於S={1,2,6,8}和d=9來說,該問題有兩個解:{1,2,6}和{1,8}。當然,這個問題的某些執行個體是無解的。

思路:把集合元素按照升序排序,即s1<=s2<=...<=sn

我們可以按照二叉樹的形式來構造狀態空間樹,樹的根代表了起點,這時還沒有對給定的元素做任何決定。根的左右子節點分別代表在當前所求的子集中包含或者不包含S1,同樣的,從第一層的節點想左走表名包含S2,向右走則不包含,以此類推。因此,從根到樹的某個第i層節點的路徑,指出了該節點所代表的的子集中包含了前i個數字中的哪些數字。

我們把這些數位和s‘記錄在節點中。如果s‘等於d,我們就找到了問題的一個解。如果s‘不等於d,當下面兩個等式中的任何一個等式成立,我們就把該節點作為沒有希望的節點終止掉。


代碼(原創):

#include <iostream>#include <algorithm>using namespace std;int arr[] = {8,4,5,6,9,2,1};  // //int arr[] = {9,8,7,6,5,4,3,2,1};//int arr[] = {1,2,3,4,5,6,7,8,9,10};const int len = sizeof(arr)/sizeof(int);bool *flag = new bool[len];bool cmp(int x,int y);void GetSubSetSum(int n,int s);int Sum(int n);int main(){int s = 10;sort(arr,arr+len,cmp);//sort by descfor(int i = 0;i<len;i++)flag[i] = false;GetSubSetSum(len-1,s);return 0;}void GetSubSetSum(int n,int s){if(s == 0)//有解{for(int i = 0;i<len;i++){if(flag[i] == true){cout << arr[i] << " ";//flag[i] = false;}}cout << endl;}if(s<arr[n] || s>Sum(n))//s過小或者過大都無解return;//狀態空間樹 分支//包含arr[n]flag[n] = true;GetSubSetSum(n-1,s-arr[n]);//不包含arr[n]flag[n] = false;GetSubSetSum(n-1,s);}int Sum(int n){int s = 0;//for(int i = 0;i<n;i++)for(int i = 0;i<=n;i++)s += arr[i];return s;}bool cmp(int x,int y){return y < x;}


6.子數組的最大和

當我們加上一個正數時,和會增加;當我們加上一個負數時,和會減少。

如果當前得到的和是個負數,那麼這個和在接下來的累加中應該拋棄並重新清零;不然的話這個負數將會減少接下來的和;

當前和大於最大和,則重設最大和;

輸入有一類特殊情況需要特殊處理。當輸入數組中所有整數都是負數時,子數組和的最大值就是數組中的最大元素。

記錄重設當前和時的數組下標和最後一次重設最大和的數組下標,可以輸出該子數組的每一個元素。

 

//子數組的最大和[演算法]  void MaxSum(int array[], unsigned int len){int beg = 0,last = 0;//開始求和  和 最後一次更新最大值的位置if(NULL == array || len <=0){return;}int curSum = 0, maxSum = 0;int i = 0;for(i=0; i<len; i++){curSum += array[i];// 累加if(curSum < 0){//-key2-- 當前和小於0,重設為0beg = i + 1;//curSum = 0;}if(curSum > maxSum){// -key1--當前和大於最大和,則重設最大和-- (3, 10, -4, 7, 2, -5,4,2)maxSum = curSum;last = i;//}}/*輸入有一類特殊情況需要特殊處理。當輸入數組中所有整數都是負數時,子數組和的最大值就是數組中的最大元素。*/if(maxSum == 0){// -key3--最大和依然為0,說明數組中所有元素都為負值maxSum = array[0];for(i=1; i<len; i++){if(array[i] > maxSum){maxSum = array[i];beg = i;last = i;}}}cout << "maxSum:" << maxSum << endl;cout << "sub arr is: " << endl;for(i = beg;i<=last;i++)cout << array[i] << " ";cout << endl;}

7.N個雞蛋放進M個籃子問題

有N個雞蛋和M個籃子,把雞蛋放到M個籃子裡,每個籃子都不可為空。另外,需要滿足:任意一個小於N的正整數,都能由某幾個籃子內蛋的數量相加的和得到。寫出程式,使得輸入一個(N,M),輸出所有可能的分配情況。

演算法問題分類---整數和問題系列總結

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.