前言
本文将介绍如何使用递归算法实现组合数。
组合数
参考题目:递归实现组合数
组合数,即Cnm,在n个数中选取m个数,不重复,一共有几种选法。
以C53为例,在1、2、3、4、5五个数中选择三个,将所有选法打印。
即一共有五个位置,每个位置都提供给我们两个选择:选,或不选。
因此,我们创建一个数组arr【25】用来保存每个位置的选择情况。
static arr[25] = { 0 };//在函数内部创建,需要放入静态区,也可设为全局变量
如果第一个位置选了,就把arr【0】设为1;如果没选,就将其设为0;
首先,我们写出主函数。
int main()
{
int n = 0;
int m = 0;
scanf("%d %d", &n,&m);
C(n,m);//即在n个数中选择m个数
return 0;
}
当我们进入C(n,m)时,首先要选择的是第一个位置,即arr中下标为0的数,而题目中要求按照字典序输出,所以我们不能以n作为当前的选择位置。
因此我们再在静态区设置一个变量step,用来存储当前我们正在选择第几个位置,当前代码如下:
void C(int n,int m)
{
static arr[25] = { 0 };//在函数内部创建,需要放入静态区
static step = 0;
}
当我们第一次进入C(n,m)后,此时step==0,即是对第一个位置选择,有两种情况,分别是选或不选。
如果选择第一个位置,那么arr【step】应该设为1,表明第一个位置我们选了。选了第一个位置以后,开始选第二个位置,所以要把step++。
既然第一个位置被选了,因为我们一共选择m个,所以进入下一个位置的选择时,参数m的位置要传入m-1才行。
此时是第一种情况,当第一种情况的所有分支都进行完以后,就要开始第二种情况的分支,但是此时arr【0】已经是1了,所以我们要把step退回至0,然后让arr【step】再设为0,表示第一个位置我们没选,才好进行没选情况下的分支,像这样:
void C(int n,int m)
{
static arr[25] = { 0 };//在函数内部创建,需要放入静态区
static step = 0;
//选
arr[step]++;
step++;
C(n,m-1);
step--;
arr[step]--;
//不选
step++;
C(n,m);
step--;
return;
}
观察此代码中间部分可以发现,以下三条语句可以合并为一条.
合并后:
arr[step-1]--;
那么,什么时候此递归才会停止呢?
因为本题中一共有五个位置,所以当step等于0、1、2、3、4时都是在进行选择,当step等于5时,就是代表所有位置我们都已经检测一遍了,可以进行判断是否要打印了。
那么打印的条件又是什么呢?因为我们只能选m个,且必须选m个。每选了一个位置,m就要-1,当m刚好等于0时,就代表着刚好选了三个位置,此时就可以打印了,像这样:
void C(int n,int m)
{
static arr[25] = { 0 };//在函数内部创建,需要放入静态区
static step = 0;
if (step == n)//五个位置都判断了
{
if (m == 0)//刚好选了m个才打印
{
int i = 0;
for (i = 0; i < n; i++)
{
if (arr[i] == 1)
{
printf("%d ", i+1);
}
}
puts("");
}
return;
}
//选
···
//不选
···
}
在本题中,因这五个数刚好是1、2、3、4、5,所有博主可以像上面那样打印出来。
如果是五个毫不相干的数,那么你可以再创建一个数组保存这五个数,利用下标来打印。
比如说:27,38,44,89,63。这五个数,那么你可以这样:
int data[5]={27,38,44,89,63};
for(int i=0;i<n;i++)
{
if(arr[i]==1)
{
printf("%d ",data[i]);
}
}
如此,就可以做到在n个随机数中选取m个的数的递归实现。
感谢您的阅读与耐心~