第二章-递归与分治策略

第一题:

问题名称:整数划分问题。

问题描述:正整数n可以表示为一系列正整数之和:n = n1 + n2 + ... + nk (k>=1, n1>=n2>=nk ),则称这种表示为正整数n的划分,不同的这种表示的个数称为正整数n的划分数,记为p(n)。在所有划分中,将最大加数n1不大于m的划分数,记为q(n,m)。

问题分析:

q(n, m) =  1      ,当n = 1,m = 1;

       q(n,n)    ,当n < m;

      1 + q(n,n-1),当n = m;

      q(n,m-1) + q(n-m,m),当n>m>1

所以得出算法:

 1 #include <stdio.h>
 2 
 3 int Division(int n, int m);
 4 void main()
 5 {
 6     int n = 0;
 7     scanf("%d", &n);
 8     printf("%d
",Division(n, n));
 9 }
10 
11 int Division(int n, int m)
12 {
13     if( ( n < 1 ) || ( m < 1 ) ) return 0;
14     if( ( n == 1 ) || ( m == 1 ) ) return 1;
15     if( n < m ) return Division(n, n);
16     if( n == m ) return Division(n, m - 1 ) + 1;
17     return Division(n, m - 1) + Division(n - m, m);
18 }
View Code

递归:

有重复元素的排列问题:

给定一个集合R={r1, ... , rn},其中的元素可能相同。求R的所有不同的排列。

算法分析:

输出的排列有重复,是因为输入的集合可能含有重复的元素。首先要学会演算,在演算的过程中,可以发现,如果递归的每一层,对于不同的递归/排列(这个就要在isRepeat函数中理解了),出现重复元素,那么产生的两个分支是完全一样的。——所以出现重复元素,那么此次就不用排列了,因为前面已经有了。

设Ri=R-{ri},集合X的全排列(所有排列)为Perm(X),而(ri)Perm(Ri)表示在Perm(Ri)的所有排列前面加上ri。

所以,R的全排列可以定义为:

    1. 出口:当n=1时,Perm(R) = r 。
    2. 产生递归:当n>1时,Perm(R) = (r1)Perm(R1),(r2)Perm(R2),... ,(rn)Perm(Rn)组成。

输入:第一行为元素个数n,第二行为待排列的n个数。

输出:前面几行是所有的不同的排列,最后一行是排列的总数。

代码:

#include <iostream>
#include <algorithm>
using namespace std;

int permNumber = 0;

bool isRepeat(char *R, int i, int k)
{
    for (int j = k; j < i; j++)
    {
        if (R[i] == R[j])
            return true;
    }
    return false;
}

void Perm(char *R, int k, int m)
{
    if (k == m)        //到最后一个元素,出口
    {
        for (int i = 0; i <= m; i++)        //因为在递归的过程中,m为变换,所以就是传进来的n-1
        {
            cout << R[i];
        }
        cout << endl;
        permNumber++;
    }
    else
    {
        for (int i = k; i <= m; i++)
        {
            if (!isRepeat(R, i, k))
            {
                swap(R[k], R[i]);
                Perm(R, k + 1, m);        //k就代表递归层数,即指向数组的位置
                swap(R[k], R[i]);
            }
        }
    }
}

int main()
{
    int n;
    char *R;
    cin >> n;
    R = new char[n];
    for (int i = 0; i < n; i++)
    {
        cin >> R[i];
    }

    cout << endl;

    Perm(R, 0, n - 1);
    cout << permNumber << endl;
    return 0;
}
/*测试数据:
输入:
4
aacc
输出:
aacc
acac
acca
caac
caca
ccaa
6
*/

集合划分问题:

一个n元集合{1,... ,n},可以划分为若干个、由m个非空集合组成的非空子集,求出指定了m的非空子集的个数——将解设为f(n,m)。m应该在[1,n]内,规定:若m=0,则表示未指明m,输出所有的非空子集的个数。

输入:只有一行、两个数,第一个数为集合个数n、第二个数为m。

输出:当输入的m在[1,n]内时,输出为由m个非空集合组成的非空子集的个数;或者当m=0时,输出所有的非空子集的个数。

代码:

分治:分解——>求解——>合并。

数字计算问题中的分治法:

strassen矩阵乘法:

组合问题中的分治法:

棋盘覆盖问题:

排序问题中的分治法:

几何问题中的分治法:

原文地址:https://www.cnblogs.com/quanxi/p/5998211.html