輸入兩個整數 n 和 m,從數列1,2,3.......n 中 隨意取幾個數
阿新 • • 發佈:2019-01-11
問題描述:輸入兩個整數n和m,從數列1,2.......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,2...n中隨意取幾個數,使其和等於m //函式引數 : n為當前最大值,m為剩餘值,flag標記選中與否,len為flag的容量 //返回值 : 無 void BagProblem_Solution1(int n, int m, int *flag, int len) { if(n < 1 || m < 1) return; if(n < m) { flag[n-1] = 1; BagProblem_Solution1(n-1, m-n, flag, len); //選了n flag[n-1] = 0; BagProblem_Solution1(n-1, m, flag, len); //不選n } else { flag[m-1] = 1; //n>=m,選中m即可 for(int i = 0; i < len; i++) { if(flag[i] == 1) cout<<i+1<<' '; } cout<<endl; flag[m-1] = 0; //不選m,繼續遞迴。比如n = 10,m = 8,求出{1, 7}後,仍需繼續,{1,3,4} {1,2,5}都是解 BagProblem_Solution1(m-1, m, flag, len); } }
//函式功能 : 從數列1,2...n中隨意取幾個數,使其和等於m //函式引數 : n為當前最大值,m為剩餘值 //返回值 : 無 void BagProblem_Solution2(int n, int m) { if(n < 1|| m < 1) return; if(n > m) n = m; int num = 1<<n; //列舉次數 for(int i = 1; i < num; i++) //列舉所有情況 { int sum = 0; int j, k; for(j = i, k = 1; j != 0; j>>=1, k++) //針對每種情況求和,判斷是否滿足條件 { if(j&1) sum += k; } if(sum == m) //如果滿足,列印結果 { for(j = i, k = 1; j != 0; j>>=1, k++) { if(j&1) cout<<k<<' '; } cout<<endl; } } }
給出一段測試程式:
int main()
{
int n, m;
cout<<"please enter n and m : ";
cin>>n>>m;
int *flag = new int[n];
for(int i = 0; i < n; i++)
flag[i] = 0;
BagProblem_Solution1(n, m, flag, n);
cout<<endl;
BagProblem_Solution2(n, m);
delete [] flag;
return 0;
}