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

http://blog.csdn.net/wuzhekai1985/article/details/6728657

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

分类: 解题笔记 3849人阅读 评论(3) 收藏 举报

      问题描述:输入两个整数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. //函数功能 : 从数列1,2...n中随意取几个数,使其和等于m   
  2. //函数参数 : n为当前最大值,m为剩余值,flag标记选中与否,len为flag的容量   
  3. //返回值 :   无   
  4. void BagProblem_Solution1(int n, int m, int *flag, int len)  
  5. {  
  6.     if(n < 1 || m < 1)  
  7.         return;  
  8.   
  9.     if(n < m)  
  10.     {  
  11.         flag[n-1] = 1;  
  12.         BagProblem_Solution1(n-1, m-n, flag, len); //选了n  
  13.         flag[n-1] = 0;  
  14.         BagProblem_Solution1(n-1, m, flag, len);   //不选n  
  15.     }  
  16.     else  
  17.     {  
  18.         flag[m-1] = 1;  //n>=m,选中m即可   
  19.         for(int i = 0; i < len; i++)  
  20.         {  
  21.             if(flag[i] == 1)  
  22.                 cout<<i+1<<' ';  
  23.         }  
  24.         cout<<endl;  
  25.         flag[m-1] = 0; //不选m,继续递归。比如n = 10,m = 8,求出{1, 7}后,仍需继续,{1,3,4} {1,2,5}都是解  
  26.         BagProblem_Solution1(m-1, m, flag, len);  
  27.     }  
  28. }  
//函数功能 : 从数列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. //函数功能 : 从数列1,2...n中随意取几个数,使其和等于m   
  2. //函数参数 : n为当前最大值,m为剩余值   
  3. //返回值 :   无   
  4. void BagProblem_Solution2(int n, int m)  
  5. {  
  6.     if(n < 1|| m < 1)  
  7.         return;  
  8.     if(n > m)  
  9.         n = m;  
  10.   
  11.     int num = 1<<n;               //枚举次数  
  12.     for(int i = 1; i < num; i++)  //枚举所有情况  
  13.     {  
  14.         int sum = 0;  
  15.         int j, k;  
  16.         for(j = i, k = 1; j != 0; j>>=1, k++) //针对每种情况求和,判断是否满足条件  
  17.         {  
  18.             if(j&1)  
  19.                 sum += k;  
  20.         }  
  21.         if(sum == m) //如果满足,打印结果  
  22.         {  
  23.             for(j = i, k = 1; j != 0; j>>=1, k++)  
  24.             {  
  25.                 if(j&1)  
  26.                     cout<<k<<' ';  
  27.             }  
  28.             cout<<endl;  
  29.         }  
  30.     }  
  31. }  
//函数功能 : 从数列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;
		}
	}
}

       给出一段测试程序:

  1. int main()  
  2. {  
  3.     int n, m;  
  4.     cout<<"please enter n and m : ";  
  5.     cin>>n>>m;  
  6.   
  7.     int *flag = new int[n];  
  8.     for(int i = 0; i < n; i++)  
  9.         flag[i] = 0;  
  10.   
  11.     BagProblem_Solution1(n, m, flag, n);  
  12.     cout<<endl;  
  13.     BagProblem_Solution2(n, m);  
  14.     delete [] flag;  
  15.     return 0;  
  16. }  
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值