解题笔记(31)——从数列1,2...n中随意取几个数,使其和等于m

      问题描述:输入两个整数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;
}

     本人享有博客文章的版权,转载请标明出处 http://blog.csdn.net/wuzhekai1985

猜你喜欢

转载自blog.csdn.net/wuzhekai1985/article/details/6728657