递归详解(STL字典序和非字典序全排列 )。

递归的定义就是函数本身调用自己,定义看起来很简单,我感觉在具体问题中是很难实现的,首先这个思想也是很难懂的,具体的过程也是抽象的,对于ACM新手是很难搞懂的。

接下来,我就几个例子来讲一下我的见解。

在数学与计算机科学中,递归(Recursion)是指在函数的定义中使用函数自身的方法。实际上,递归,顾名思义,其包含了两个意思:,这正是递归思想的精华所在。

递归:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门,你继续打开它。若干次之后,你打开面前的门后,发现只有一间屋子,没有门了。然后,你开始原路返回,每走回一间屋子,你数一次,走到入口的时候,你可以回答出你到底用这你把钥匙打开了几扇门。

循环:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门(若前面两扇门都一样,那么这扇门和前两扇门也一样;如果第二扇门比第一扇门小,那么这扇门也比第二扇门小,你继续打开这扇门,一直这样继续下去直到打开所有的门。但是,入口处的人始终等不到你回去告诉他答案。

递归问题必须可以分解为若干个规模较小,与原问题形式相同的子问题。找到 原问题与子问题的 的连接点,继续往 下一状态进行操作,如果 说 原问题有什么 操作 ,那么 子问题 也是会 进行相同的操作,这时,就要对状态的改变 和要进行的操作 要进行仔细的 判断 ,是否可以递归,并进行 操作 的 思考模拟 ,是否可行 。

对初试状态 操作 要明确,然后你才能保证 每一个 子问题,都可以按照 正确的

递归其实就是 递与归 ,先递去,然后再 归来。

看一下 下面 这个 题目:

全排列:这类问题应该是ACM新手最头疼的问题,用到递归回溯,知识点,很快你就进入了迷的状态。对于 abc 这个 字符串,所有的全排列 是 abc acb bac bca cba cab 看样子 是固定一位字符,将后面的 字符 与它 换换 位置 ,如果后面还有的话,就继续固定 下一个字符,直到 最后剩下一个 的 时候 ,就去回溯,执行递去的每步操作 ,比如 123  这个字符串,先进 最外层的 ,就是 交换 第一位的 ,发现 可以再进一层 就  是 交换 第二位的 字符 ,再进 ,发现 是最后一个 了 ,就退出 ,回溯 ,进行 第二层 交换的操作,完了后 ,退回到 第一层 ,进行 第一位 的操作,就这样 一直操作,直到第一位 的字符 已经 过了 a b c 一遍后 ,就退回 主函数 , 在这 过程中 ,可以 进行 操作 ,输出 每个 序列 。

输出字符串的全排列:


#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
const int maxn=105; 
int count=0;
void all_pailie(char *A,int k,int n)
{
	int i;
	if(k==n)
		{
			count++;
			for(i=0;i<=n;i++)
				cout<<A[i]<<' ';
			cout<<endl;		
		}
	else
	{
		for(i=k;i<=n;i++)
		{
			swap(A[i],A[k]);
			all_pailie(A,k+1,n);
			swap(A[i],A[k]);	
		}
	}	
}
int main()
{
	int i;
	char n[maxn];
	cin>>n;
	all_pailie(n,0,strlen(n)-1);
	cout<<count<<endl;	
	return 0;
}

很明显,这个代码是有BUG的,如果你的字符串中有重复的字符的话,程序也会看作是不同的字符去排列,如果题目有具体的要求,这个解法肯定是行不通的,还要进行去重操作。

接下来,看看 去重操作:

代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
const int maxn=105; 
int count=0;
char a[maxn];
bool check(int x,int y)
{
	for(int i=x;i<y;i++)
		if(a[i]==a[y])
			return true;
	return false;
}
void all_pailie(char *A,int k,int n)
{
	int i;
	if(k==n)
		{
			count++;
			for(i=0;i<=n;i++)
				cout<<A[i]<<' ';
			cout<<endl;		
		}
	else
	{
		for(i=k;i<=n;i++)
		{
			if(!check(k,i))
			{
				swap(A[i],A[k]);
				all_pailie(A,k+1,n);
				swap(A[i],A[k]);	
			}
		}
	}	
}
int main()
{
	int i;
	cin>>a;
	all_pailie(a,0,strlen(a)-1);
	cout<<count<<endl;	
	return 0;
}

数字的全排列:输入 整数,输出他的全排列 ,这个应该不会出重复的问题。就是一个 整数n ,从1 到 n的 排列。


#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
const int maxn=105; 
int count=0;
void all_pailie(int *A,int k,int n)
{
	int i;
	if(k==n-1)
		{
			count++;
			for(i=0;i<n;i++)
				cout<<A[i]<<' ';
			cout<<endl;		
		}
	else
	{
		for(i=k;i<n;i++)
		{
			swap(A[i],A[k]);
			all_pailie(A,k+1,n);
			swap(A[i],A[k]);	
		}
	}	
}
int main()
{
	int i;
	int n;
	int a[maxn];
	cin>>n;
	for(i=0;i<n;i++)
		a[i]=i+1;
	all_pailie(a,0,n);
	cout<<count<<endl;	
	return 0;
}

对于STL 的解法 就更容易了 ,就直接 调用函数 就可以了 。

代码: 对于这个 函数名 ,是不能变的,一变就会报错,这种解法,可以去重,还可以 按字典序来排。集很多功能于一体 的简单写法。,但还是 需要 每次先使数组 是递增的顺序才可以,用sort 就可以。

这是 去重 和 递增的 字典序。用 next_permutation

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn=105;
void permutation(char* str,int length)
{
	sort(str,str+length);       
	do
	{
		for(int i=0;i<length;i++)
			cout<<str[i]<<' ';
		cout<<endl;
	}while(next_permutation(str,str+length));

}
int main()
{
	char str[maxn];    //字符串 
	cin>>str;
	permutation(str,strlen(str));  //函数 参数是函数名,和长度     
	return 0;
}

当要求 递减的时候 ,就需要先把数组降序,才可以使用prev_permutation,就需要 prev_permutation

代码:

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn=105;

bool cmp(int a,int b)
{
	return a>b;
}

void permutation(char* str,int length)
{
	sort(str,str+length,cmp);       
	do
	{
		for(int i=0;i<length;i++)
			cout<<str[i]<<' ';
		cout<<endl;
	}while(prev_permutation(str,str+length));

}
int main()
{
	char str[maxn];    //字符串 
	cin>>str;
	permutation(str,strlen(str));  //函数 参数是函数名,和长度     
	return 0;
}

这样减少了时间去敲 递归的 代码了,还要考虑去重 和 字典序和

非字典序。

来道题:

题面:Bob and math problem HDU - 5055

Recently, Bob has been thinking about a math problem.
There are N Digits, each digit is between 0 and 9. You need to use this N Digits to constitute an Integer.
This Integer needs to satisfy the following conditions:

  • 1. must be an odd Integer.
  • 2. there is no leading zero.
  • 3. find the biggest one which is satisfied 1, 2.


Example:
There are three Digits: 0, 1, 3. It can constitute six number of Integers. Only "301", "103" is legal, while "130", "310", "013", "031" is illegal. The biggest one of odd Integer is "301".

Input

There are multiple test cases. Please process till EOF.
Each case starts with a line containing an integer N ( 1 <= N <= 100 ).
The second line contains N Digits which indicate the digit a1,a2,a3,⋯,an.(0≤ai≤9)a1,a2,a3,⋯,an.(0≤ai≤9).

Output

The output of each test case of a line. If you can constitute an Integer which is satisfied above conditions, please output the biggest one. Otherwise, output "-1" instead.

Sample Input

3
0 1 3
3
5 4 2
3
2 4 6

Sample Output

301
425
-1

代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=105;
char a[maxn];
char b[maxn];
bool cmp(char a,char b)
{
	return a>b;
}
bool permutation(char *str,int length)
{
	sort(str,str+length,cmp);
	do
	{
		if(str[length-1]%2==1&&str[0]!='0')
		{
			for(int i=0;i<length;i++)
				cout<<str[i];
				cout<<endl;
				return true;	
		}
	}while(prev_permutation(str,str+length));	
	return false;
}
int main()
{
	int num;
	while(cin>>num)
	{
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		for(int i=0;i<num;i++)
			cin>>b[i];
		strcpy(a,b);
		if(!permutation(a,strlen(a)))
			cout<<"-1"<<endl;
	//	else
	//		cout<<endl;		
	}
	return 0;
}

原理就是上面的那个 函数 。是不是 很简单 。希望大家 多学习。

猜你喜欢

转载自blog.csdn.net/tsam123/article/details/85058119