计算方法之递归分治


一.全排列问题


问题描述:
有一组数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;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值