从n个数中抽取r个数有多少种组合(109)

问题: 假设有n个数,这里假设为5个数,a[5]={1,2,3,4,5},从中抽取3个数有多少种情况?(3个数不能完全重复)

思路:肯定要用一个数组a存储这n个数,其次需要一个数组b存储选出的这个数每一位的值对应于a数组的下标是多少,因此b数组大小肯定为r,首先这5个数存储在下标0~4上,对待这种问题我们应该从特殊到一般去想,假设第一个选取的组合数字为5(下标为4),那么我在选第二个组合数字时选取的范围是不是变成了a数组下标0~3中了,第二个选取的组合数字为4(下标为3),同样,我选取第三个组合数字时选取范围变成下标0~2了,第三个选取的组合数字为3(下标为2),此时第一种情况已经出现了,将其输出,可以发现这个数的每一位对应a数组下标为234,那么我们保证后两位不动,还有134、034两种情况,这三种情况是基于后两位不动情况下得到的,下一步该基于第一位和第三位不动,变换第二位,得出421、420、410三种情况,最后一步基于12两位不动变换第3位得到,321、320、310,210四种情况,一共合计10种情况,以下则为代码

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int *a = NULL, an;
	int *b = NULL, bn;
	int R, sum = 0;
	
	printf("请输入a数组的元素个数:\n");
	scanf("%d", &an);
	a = (int *)malloc(sizeof(int)*an);
	printf("请为这%d个数依次赋值\n", an);
	for (int i = 0; i < an; i++)
	{
		scanf("%d", &a[i]);
	}
	printf("你打算每次从这%d个数中取几个数?\n", an);
	scanf("%d", &bn);
	b = (int *)malloc(sizeof(int)*bn);
	R = bn;
	int combine(int, int, int, int*, int *,int &);
	sum=combine(an, bn, R, a, b,sum);
	printf("共计%d个\n", sum);
	return 0;
}
int combine(int an, int bn, int R, int *a, int *b,int &sum)
{
	if (bn == 0)
	{
		for (int i = 0; i < R; i++)//设置一个R的目的是bn一直在参与递归,时刻在变,用R来保存抽取的个数
		{
			printf("%d", a[b[i]]);//b[i]中存放某个组合数在a数组中的下标
		}
		sum++;
		printf("\n");
	}
	else
	{
		for (int j = an; j >= bn; j--)//记下这组循环条件,它指明了选取每个组合数时会有几种情况(本题中每趟循环三次,证明每个组合数有3种情况),以后类似题可以类比此条件
		{
			b[bn - 1] = j - 1;//别写成b[bn-1]=an-1
			combine(j - 1, bn - 1, R, a, b,sum);//别写成combine(an-1,bn-1,R,a,b,sum)
		}
	}
	return sum;
}

运行结果

在这里插入图片描述

else内的第一遍循环是为了寻找第一个组合数的情况(b[2]),显然有3种,为什么是3种呢,因为你不光要找到第一个组合数,还要满足是三位数的条件,所以是345三个数中的一个,第二遍循环是为了寻找第二个组合数的情况(b[1]),相当于从剩余的4个数里选一个数放进b[1],且满足是二位数的条件,为什么不是三位数了呢,因为第一层循环已经找到一个数了,显然也是3种情况,第三遍循环是为了寻找第三个组合数的情况(b[0]),相当于从剩余3个数中选一个放进b[0],不需满足额外条件,因为前面已经选出两个数了,此时剩几个数就有一种情况,显然还是3种,并不是说结果就是3+3+3=9种,这里说的每个三种只不过是每个组合数可能的取值情况,最终选出一个数还要经过各组合数之间相互组合,那么递归出口应该怎么理解呢,我们将combine(5,3,3,a,b)理解成当前a数组有5个数,且待组合数为3,那么当combine(3,0,3,a,b),我们理解成a数组剩余3个数,待组合数为0,那不就证明每个组合数都已经选出了么(b数组已全部赋值),打印此数即可,所以递归出口条件设置为当前待组合数为0时结束本层递归

反思:递归的实现首先要把大问题转化为可执行的小问题,且大小问题解决方法相同,所以在我看来,这道题中要抽取3个组合数,那么确定第一个、第二个、第三个组合数就是这道题的小问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值