递归实现指数型、组合型、排列型枚举,按字典序输出

指数型枚举:

题目描述:

从1~n这n个整数中随机选取任意多个,每种方案里的数从小到大排列,按字典序输出所有可能的选择方案。

输入:

​输入一个整数 n (1≤n≤10)

输出:

​每行一组方案,每组方案中两个数之间用空格分隔。

注意每行最后一个数后没有空格。

输入样例:

4

输出样例:
1
1 2
1 2 3
1 2 3 4
1 2 4
1 3
1 3 4
1 4
2
2 3
2 3 4
2 4
3
3 4
4

题解:

按位枚举即可,设枚举的位数是 i,该位最小可枚举的是 j,该位枚举到 n 为止

我们需要一个数组来存放每位的数据。一开始我没开数组,结果写了半天hh

所以我们需要设计的函数有 4 个参数,f(int i, int j, int n, int* pa)

每位的数字,每次枚举完都需要输出,可以将此单独写成一个函数,print_ans(int i, int* pa)

每次枚举完枚举下一位,递归,直到 n 结束

代码:

#include <stdio.h>
void print_ans(int i, int* pa)
{
	int k;
	for (k = 0; k < i; k++)
	{
		printf("%d", pa[k]);
		if (k < i - 1)//注意题目要求,最后不能有空格
			printf(" ");
	}
	printf("\n");
}
void f(int i, int j, int n, int* pa)
{
	int k;
	for (k = j; k <= n; k++)//枚举j~n,按这样的顺序就是字典序了
	{
		pa[i - 1] = k;
		print_ans(i, pa);//每次枚举完输出
		if (k < n)//到k为止,否则继续递归
			f(i + 1, k + 1, n, pa);
	}
}
int main()
{
	int n, a[10] = { 0 };
	scanf("%d", &n);
	f(1, 1, n, a);//从第1位、1开始
	return 0;
}

组合型枚举:

题目描述:

​ 从 1−n 这 n 个整数中随机选取 m 个,每种方案里的数从小到大排列,按字典序输出所有可能的选择方案。

输入:

​ 输入两个整数 n,m。(1≤m≤n≤10)

输出:

​ 每行一组方案,每组方案中两个数之间用空格分隔。

​ 注意每行最后一个数后没有空格。

输入样例:

5 3

输出样例:

1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5

题解:

跟指数型枚举差不多。其实我们只要在指数型枚举的基础上

引入变量 l,写在参数里,每次输出前加一个判断 if(i == l) 就行了

还能再优化一下,i >= l 便不再递归

代码:

#include <stdio.h>
void print_ans(int i, int* pa)
{
	int k;
	for (k = 0; k < i; k++)
	{
		printf("%d", pa[k]);
		if (k < i - 1)
			printf(" ");
	}
	printf("\n");
}
void f(int i, int j, int n, int l, int* pa)
{
	int k;
	for (k = j; k <= n; k++)
	{
		pa[i - 1] = k;
		if(i == l)//引入变量l,符合该条件再输出就行了
			print_ans(i, pa);
		if (k < n && i < l)//递归条件还可以优化,加一条 i < l,减少递归次数
			f(i + 1, k + 1, n, l, pa);
	}
}
int main()
{
	int n, l, a[10] = { 0 };
	scanf("%d%d", &n, &l);
	f(1, 1, n, l, a);
	return 0;
}

排列型枚举:

题目描述:

​ 从 1−n 这 n 个整数排成一排并打乱次序,按字典序输出所有可能的选择方案。

输入:

​ 输入一个整数 n。(1≤n≤8)

输出:

​ 每行一组方案,每组方案中两个数之间用空格分隔。

​ 注意每行最后一个数后没有空格。

输入样例:

3

输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

题解:

跟前两个也差不多

因为不要求后一个数大于前一个数,所以不再需要 j

用数组 b[10] 取而代之,引入它来表示各数字是否已被使用,b[m]表示m+1

使用过的数字就标记,返回后解除标记

代码:

#include <stdio.h>
void print_ans(int i, int* pa)
{
	int k;
	for (k = 0; k < i; k++)
	{
		printf("%d", pa[k]);
		if (k < i - 1)
			printf(" ");
	}
	printf("\n");
}
void f(int i, int n, int* pa, int* pb)
{
	int k;
	for (k = 1; k <= n; k++)
	{
		if (!pb[k - 1])
		{
			pb[k - 1] = 1;//标记被使用过的数字
			pa[i - 1] = k;
			if (i == n)
				print_ans(i, pa);
			if (k <= n && i < n)
				f(i + 1, n, pa, pb);
			pb[k - 1] = 0;//返回后解除标记
		}
	}
}
int main()
{
	int n, a[10] = { 0 }, b[10] = { 0 };
	scanf("%d", &n);
	f(1, n, a, b);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值