一.全排列问题
问题描述:
有一组数R,需要输出它的全排列。R的递归可定义如下:
当个数n为1时,Perm® = ®,其中r是集合R中唯一的元素
当个数n大于1时,Perm®由(r1)Perm(R1),(r2)Perm(R2),(r3)Perm(R3),…,(rn)Perm(Rn)构成
其中Ri = R - {ri} 即该集合中减去对应元素
思路:
看上面的问题描述几乎就懵掉了。说的乱乱的,我在这用通俗易懂的方法解释一下。比如说有四个数1,2,3,4,那也就是说对应有四个位置。首先第一个位置有四种选择,可以放1,2,3,4。当放1的时候,还剩下3个位置对应三个数2,3,4,那第二个位置就对应3个选择2,3,4;当第二个位置放2的时候还剩下两个数3,4,此时第三个位置有两个选择放3或4;当我们放3的时候,还剩下一个位置一个数4,那第一种就排好了1234,下一种情况就回到了刚刚第三个数两种选择,3我们选择过了,现在选择4放在第三个位置,那第四个位置也就确定了,此时是1243,再回到第二个位置选择,以此类推。
输出的顺序是:
1234
1243
1324
1342
1432
1423
2134
2143
2314
2341
2431
2413
3214
3241
3124
3142
3412
3421
4231
4213
4321
4312
4132
4123
代码:
public class Demo {
public void Perm(int list[], int k, int m) {
if (k == m) {
for (int i = 0; i <= m; i++)
System.out.print(list[i]);
System.out.println();
} else {
for (int i = k; i <= m; i++) {
// 从固定的数后第一个依次交换
Swap(list, k, i);
Perm(list, k + 1, m);
// 这组递归完成之后需要交换回来
Swap(list, k, i);
}
}
}
public void Swap(int[] list, int i, int j) {
int t = list[i];
list[i] = list[j];
list[j] = t;
}
public static void main(String[] args) {
Demo d = new Demo();
int[] arr = {1,2,3,4};
d.Perm(arr, 0, 3);
}
}
二.整数划分问题
问题描述:
将正整数n表示成一系列正整数之和:n=n1+n2+…+nk,
其中n1≥n2≥…≥nk≥1,k≥1。
正整数n的这种表示称为正整数n的划分。求正整数n的不
同划分个数。
例如正整数6有如下11种不同的划分:
6;
5+1;
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1。
思路:
这道题想必最开始看到老师给的递归公式都是一脸懵吧(可能只有我这样的菜鸡才一脸懵),不要慌,我在这里通俗易懂的解释下。
首先就是这个q(n,m),表示的是把n划分为 加数小于或等于某个数的划分,在这里把这个数成为m。
比如说q(6,6)就是
6;
5+1;
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1;
而q(6,5)就是
5+1;
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1;
好,这个q(n,m)已经懂了,那接下来就应该分类讨论了。
(1)当n=1时 q(1,m):表示是对1的划分,那么只有一种划分方式 1
(2)当m=1时q(n,1):当m=1时其实就是把让所有加数小于等于1,那就是所有加数都是1,当然也只有一种划分方式
(3)当n==m时q(n,n):此时就是对n的划分出来的数没有限制,默认限制就是不大于n,此时划分的总类数要分两种情况才比较好解决:
1.划分出来的数包含n(或m,因为n==m):那只有一种方式 比如 6的划分 只有 6;一种方式
2.划分出来的数不包含n(或m,因为n==m):就可以认为是将6划分出来的数都小于6,其实就是都小于或等于5,接下来 其实就是求出来q(n,n-1)或者是q(n,m-1),此时n>m-1,放到递归方程里就是求解q(n,m) n>m
综合起来 q(n,n)=1+q(n,m-1)
(4)当n>m时:当遇到这个问题时,其实可以看做是对n的划分有了条件,就是所有的划分出来的数小于m,在上文中,6有11种划分方式,那是没有对6划分出来的数进行限制,当要使划分出来的数都小于某个数时比如5时,那就不是11种了。
5+1;
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1;
这个时候分两种情况:包含m和不包含m :
不包含m就是:
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1;这些情况,其实就是求6的划分出来的数小于等于4的情况,放到递归方程就是 q(n,m-1)
包含m就是:
5+1;
这个时候,确定这种划分数量的时候m不再是主角,我们只要求出来n-m的划分情况就行了,因为此时的m的划分情况,取决于n-m。 比如包含m=5的划分情况就是 6-5=1的情况,比如包含4的划分情况就是求6-4=2,2的没有限制的划分情况。放到递归方程里面就是q(n-m,m)
(5)q(n,m)n<m:n<m时,比如n=6,m=7 求得就是6得划分数小于等于7的情况,其实就是求解小于等于6,故此时情况就是求解q(n,n);
代码:
#include<iostream>
#include<ctime>
using namespace std;
int q(int n,int m)
{
if(n==1||m==1){
return 1;
}else if(n>m){
return q(n-m,m)+q(n,m-1);
}else if(n<m){
return q(n,n);
}else{
return 1+q(n,n-1);
}
}
int main()
{
double start,stop,dur;
int a=6;
cout<<q(6,6);
return 0;
}
三.Hanoi问题
这个题我们就不叙述了,题意谁都懂,递归的想法也很好理解,我就随便提个代码吧。
#include <bits/stdc++.h>
int count=0;
void hanoi(int n, char A, char B, char C) //n为盘子的个数,A是最左边柱子,C是最右边柱子,B是中间柱子;
{
if(n==1){
printf("%d\n",++count); //只有一个盘子,直接从A移动到C;
return;
}
hanoi(n-1, A, C, B); //n-1个盘子,把前n-1个移动到B柱子,最后一个移动到C柱子,再把n-1个从B移动到C;
hanoi(n-1, B, A, C); //第n个盘子移完后,从B上把n-1个移到C上;
}
int main()
{
int n;
scanf("%d",&n);
hanoi(n, 'A', 'B', 'C');
return 0;
}