1. 程式人生 > >從數列1,2,3,......,n中隨意取出幾個數,使其和等於m

從數列1,2,3,......,n中隨意取出幾個數,使其和等於m

問題描述:
    輸入兩個整數n和m,從數列1,2,3,.......n中隨意去幾個數,使其和等於m,要求將其中所有可能的組合列出來。

解決思路
    這個問題其實是揹包問題的變形,給出兩種解決方法。
    解法一:
    用遞迴,效率可能低了點。假設問題的解為F(n,m),可分為兩個子問題F(n-1,m-n)和F(n-1,m)。對於這兩個問題遞迴求解,求解的過程中,如果找到了符合條件的數字組合,則打印出來。
    解法二:
    用迴圈,其實就是列舉所有組合。對於n,組合數應該為2^n。我們可以用一個數字i來表示組合。如果i = 5,其二進位制形式為101,相應的組合為{1,3}。也就是說,二進位制的每一位都表示一個數字,bit0代表數字1,bit1代表數字2,依次類推。當某位為1,表示選中了該位所表示的數字。


參考程式碼
  1. #include <iostream>
  2. using namespace std;
  3. //函式功能:從數列1,2,3......n中隨意取幾個數,使其和等於m
  4. //函式引數:n為當前最大值,m為剩餘值,flag標記選中與否,len為flag的容量
  5. //返回值: 無
  6. void BagProlem_Solution1(int n, int m, int *flag, int len)
  7. {
  8.     if(< 1 || m < 1)
  9.     {
  10.         return;
  11.     }
  12.     if(< m)
  13.     {
  14.         flag[- 1] =
     1;
  15.         BagProlem_Solution1(- 1, m - n, flag, len); //選了n
  16.         flag[- 1] = 0;
  17.         BagProlem_Solution1(- 1, m, flag, len);     //不選n
  18.     }
  19.     else
  20.     {
  21.         flag[- 1] = 1; //>= m,選中m即可(n = m)
  22.         for(int i = 0; i < len; i++)
  23.         {
  24.             if(flag[i] == 1)
  25.             {

  26.                 cout << i + 1 << " ";
  27.             }
  28.         }
  29.         cout << endl;
  30.         flag[- 1] = 0; //不選m,繼續遞迴。比如n = 10, m = 8,求出{1,7}後,仍需繼續,{1,3,4}{1,2,5}都是解
  31.         BagProlem_Solution1(- 1, m, flag, len);
  32.     }
  33. }
  34. //函式功能:從數列1,2,...n中隨意選取幾個數,使其和等於m
  35. //函式引數:n為當前最大值,m為剩餘值
  36. //返回值: 無
  37. void BagProlem_Solution2(int n, int m)
  38. {
  39.     if(< 1 || m < 1)
  40.     {
  41.         return;
  42.     }
  43.     if(> m)
  44.     {
  45.         n = m;
  46.     }
  47.     int num = 1 << n;    //列舉次數
  48.     for(int i = 1; i < num; i++)
  49.     {
  50.         int sum = 0;
  51.         int j, k;
  52.         for(= i, k = 1; j != 0; j >>= 1, k++)
  53.         {
  54.             if(& 1)
  55.             {
  56.                 sum += k;
  57.             }
  58.         }
  59.         if(sum == m)//如果滿足,列印結果
  60.         {
  61.             for(= i, k = 1; j != 0; j >>= 1, k++)
  62.             {
  63.                 if(& 1)
  64.                 {
  65.                     cout << k << " ";
  66.                 }
  67.             }
  68.             cout << endl;
  69.         }
  70.     }
  71. }
  72. int main()
  73. {
  74.     int n, m;
  75.     cout<<"please enter n and m : ";
  76.     cin>>n>>m;
  77.     int *flag = new int[n];
  78.     for(int i = 0; i < n; i++)
  79.     {
  80.         flag[i] = 0;
  81.     }
  82.     BagProlem_Solution1(n, m, flag, n);
  83.     cout<<endl;
  84.     BagProlem_Solution2(n, m);
  85.     delete [] flag;
  86.     return 0;
  87. }
    上述方法二,思路確實很巧妙,但是有一定的限制,對於32位機器來說,只能列舉32個數。