1、题目描述:输入两个整数n和m,从数列1,2,3.......n 中随意取几个数,这里同一个数不能重复取,使其和等于m,要求将其中所有的可能组合列出来。
解题思路:典型的0-1背包问题,每一个数字都有两种状态,取或者不取。最简单的就是用递归求解。这里的遍历顺序是从n到1,这样比较好判断递归的出口,当然也比较方便剪枝。核心代码如下,有没有觉得很漂亮:
*每一种数字的数量不限,也就是可以无限次的取。
*每一种数字都给定一个数量,只能在数量范围之内取。这种情况就类似于任意一个多项式的展开。
核心代码如下:
解题思路:典型的0-1背包问题,每一个数字都有两种状态,取或者不取。最简单的就是用递归求解。这里的遍历顺序是从n到1,这样比较好判断递归的出口,当然也比较方便剪枝。核心代码如下,有没有觉得很漂亮:
//找到1~Num中和等于Sum的所有组合
void FindCombination(int *arr, int Index, int Sum, int Num)
{
if(Sum < 1 || Num < 1)
return;
if(Sum < Num)
Num = Sum;
if(Sum == Num || Sum == 1)
{
arr[Index++] = Sum;
Print(arr, Index);
--Index;
return;
}
//不取当前最大值Num
FindCombination(arr, Index, Sum, Num - 1);
arr[Index++] = Num;
//取当前最大值Num
FindCombination(arr, Index, Sum - Num, Num - 1);
}
2、题目扩展,上面一个题要求每一个数字只能取一次,要是每个数字可以随便取多少次,那这样的组合有多少种呢?这种情况的数据很大,所以不需要输出具体的组合,只需要给出种类的数量就可以了,博客
钱币兑换问题就是用这个方法求解的。这种情况下就用母函数来求解。关于母函数的相关知识,有时间了我再补充上来,如果大家不懂,可以先留着,下次再来看。关于这种题目有几种应用情况,以前写过的,不过代码不知道丢到哪里了,下次补上:
*每一种数字的数量不限,也就是可以无限次的取。
*每一种数字都给定一个数量,只能在数量范围之内取。这种情况就类似于任意一个多项式的展开。
核心代码如下:
//允许数字重复,找出所有1~Num组成的和为Sum的数
int GetNumOfSum(int Sum, int Num)
{
if(Sum < 1 || Num < 1)
return 0;
if(Num > Sum)
Num = Sum;
int c1[N];
int c2[N];
int i,j,k;
for(i = 0; i <= Sum; ++i)
c1[i] = 1;
memset(c2, 0, (Sum + 1) * sizeof(int));
for(i = 2; i <= Num; ++i)
{//遍历所有的因子
for(j = 0; j <= Sum; ++j)
{//遍历所有的指数
for(k = 0; k + j <= Sum; k += i)
{//用当前指数去乘当前因子组成的多项式
c2[k + j] += c1[j];
}
}
memcpy(c1, c2, (Sum + 1) * sizeof(int));
memset(c2, 0, (Sum + 1) * sizeof(int));
}
return c1[Sum];
}
3、最后给出变量的定义和main函数的调用。
#include<stdio.h>
#include<string.h>
const int N = 30;
//打印数组
void Print(int *arr, int nLen)
{
if(!arr || nLen < 1)
return;
for(int i = 0; i < nLen; ++i)
printf("%d ", arr[i]);
printf("\n");
}
int main()
{
int arr[N];
int n,m;
while(scanf("%d %d", &n, &m) != EOF)
{
//FindCombination(arr, 0, m, n);
printf("可重复,则一共有%d种\n", GetNumOfSum(m, n));
}
return 0;
}