codeup-5973 【递归入门】组合的输出题解——关于怎么非递归解这道题

组合的输出

排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r < = n),我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。 
现要求你不用递归的方法输出所有组合。 
例如n = 5 ,r = 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 

输入

一行两个自然数n、r ( 1 < n < 21,1 < = r < = n )。

输出

所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,所有的组合也按字典顺序。

 

这道题,咳咳,上来反手就是dfs递归。

咳咳,啥?非递归????!!!!!

想了半天还是莫得思路,后来从博客上面看到了有相关的想法,我就自己按照该想法写了

我是按照从下标1开始进行读取,到读完下标n结束的,即第几个下标就是几。

这道题,你仔细观察,组合,无序状态,你就按照从小到大排序输出(虽然题目也是叫你这么干的),不会重复不会漏,首先打一把1,2,3,这个循环就能实现的,接下来怎么做,你可以发现后面变成了1,2,4;1,2,5,显然是末尾++了,加到什么时候能结束呢?(dfs递归的时候必问:什么时候能结束?)那就是第r - i个等于n - i的时候,为什么?你第r - i个都是n - i了,你后面就n - i + 1, n - i + 2, ......, n,就这一种情况,你要是大于n - i,你让后面的兄弟怎么搞啊,对不对。好,退出,什么时候结束呢,当循环退出时i == r,不好意思,你的这时候已经到达第0个了,你总不能把第0位加一吧,我们是从第一位开始读取的,所以不存在第0位,这个时候退出。

好这个时候你知道进位了,进位完你怎么办呢?你仔细观察1,3,4;1,3,5,题目样例在进位完之后后面的数字都是比前一个多一,当然这样是比上一个大的最小集合,满足条件,也找到了套路,进位完的后面就一次比前面多一,由于该位最大为r - i,所以后面不会超过范围,然后一次这样。

AC代码:

#include <stdio.h>
int ans[22];
bool vis[22];
int n, r;
bool flag;
void print()
{
	for(int k = 1; k <= r; k++)
	{
		if(k != r)
			printf("%d ", ans[k]);
		else 
			printf("%d\n", ans[k]);
	}
}
void solve()
{
	if(ans[r] < n)
		ans[r]++;
	else
	{
		int i = 1;
		while(ans[r - i] == n - i && r - i > 0)
		{
			i++;
		}
		if(r == i)
		{
			flag = false;
			return;
		}
		ans[r - i]++;
		for(int j = r - i + 1; j <= r; j++)
		{
			ans[j] = ans[j - 1] + 1;
		}
	}
	print();
}
int main(void)
{
	while(scanf("%d %d", &n, &r) == 2)
	{
		flag = true;
		for(int i = 1; i <= r; i++)
		{
			ans[i] = i;
		}
		print();
		while(flag)
			solve();
	}
	return 0;
}

 

  总结:还是递归大法好,dfs题来时命能保(逃),非递归的话应该在一次调用函数时就算到递归找到解时的状态(例如本题调用一次函数就找到了一个答案),采用while循环控制什么时候能退出,在找到一次答案时也要在函数里面采用while循环模拟每次递归进行的操作(例如本题中while进行进位和末尾++)。

 

posted @ 2019-08-13 17:08  funforever  阅读(493)  评论(0编辑  收藏  举报