【笔试题】输入两个整数n和m,从数列1,2,3…n中随意取几个数使其和等于m

【笔试题】输入两个整数n和m,从数列1,2,3…n中随意取几个数使其和等于m



1、题目

  输入两个整数n和m,从数列1,2,3,……n中随意取几个数,使其和等于m。要求将所有的可能组合列出来。


2、分析

  这是中兴某年的一道笔试题。其实本质就是0/1背包问题。对于每一个n,我们先考虑是否取n。

  • 取n,子问题就变成find(n-1,m-n)

  • 不取n,子问题就变成find(n-1,m)

  这样就可以把find(n,m)问题拆分成find(n-1,m-n)和find(n-1,m),利用DP的思想找到了递归的递推式。注意这里的终止条件是n<1或者m<1就结束了。

理解:

  这里我采用的是flag数组,数组的长度是n,与元素1,2,3 … n一一对应。数组元素表示只有两种情况,1代表选择了对应的数,0代表未选择对应的数。当n>=m时,直接从m=n处开始。就拿n=10,m=8来说,我们可以选择8或不选8,7,6 ……依次类推,直到选择的数相加和为m。

image-20210318215637485


3、代码

#include<iostream>
using namespace std;
void find(int n, int m, int *flag, int leng)
{
    
    
	if (n < 1 || m < 1)
		return;
	if (n < m)
	{
    
    
		flag[n - 1] = 1;       //选了n
		find(n - 1, m - n, flag, leng); 
		flag[n - 1] = 0;       //不选n
		find(n - 1, m, flag, leng);   
	}
	else
	{
    
    
		flag[m - 1] = 1; //n >= m,选中m即可(n = m)
        //输出打印
		for (int i = 0; i < leng; 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}都是解
		find(m - 1, m, flag, leng);
	}
}
int main()
{
    
    
	int n, m;
	cin >> n >> m;
	int *flag = new int[n];
	for (int i = 0; i < n; i++)
		flag[i] = 0;
	find(n, m, flag, n);
	cout << endl;
	delete[] flag;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Sunnyside_/article/details/114992638