1. 程式人生 > >輸入兩個整數 n 和 m,從數列1,2,3.......n 中 隨意取幾個數

輸入兩個整數 n 和 m,從數列1,2,3.......n 中 隨意取幾個數

問題描述:輸入兩個整數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;
}