【笔试题】输入两个整数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。
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;
}