递归与迭代_3 2016.6.10

1、经典的汉诺塔问题

汉诺塔小游戏:http://www.4399.com/flash/109504_1.htm

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;

const int mod = 1e9 + 7;
const int INF = 0x7fffffff;
int n;

void Hanoi(int num, char x, char y, char z);

int main()
{
    scanf("%d", &n);
    char x = 'x', y = 'y', z = 'z';     //有 x、y、z三根柱子
    Hanoi(n, x, y, z);      //借助 y 将 n 个盘子从 x 移动到 z
    return 0;
}

void Hanoi(int num, char x, char y, char z)
{
    if (num == 1) {
        printf("%c -> %c\n", x, z);
        return;
    } else {
        Hanoi(num-1, x, z, y);          //借助 z 将 num-1 个盘子先移动到 y 上
        printf("%c -> %c\n", x, z);     //然后将第 num 个盘子直接从 x 移动到 z
        Hanoi(num-1, y, x, z);          //最后再借助 x 将 y 上的 num-1 个盘子移动到 z上
    }
}

2、兔子繁殖问题

有一对新生的兔子,从第三个月开始他们每个月都生一对兔子,新生的兔子从第三个月开始又每个月生一对兔子。
 按此规律,并假定兔子没有死亡,20个月后共有多少个兔子?

斐波那契数列问题
 f(1) = 1(第1个月有一对兔子)
 f(2) = 1(第2个月还是一对兔子)
 f(3) = 2(原来有一对兔子,第3个开始,每个月生一对兔子)
 f(4) = 3(原来有两对兔子,有一对可以生育)
 f(5) = 5(原来有3对兔子,第3个月出生的那对兔子也可以生育了,那么现在有两对兔子可以生育)
 f(6) = 8(原来有5对兔子,第4个月出生的那对兔子也可以生育了,那么现在有3对兔子可以生育)
 ..............
 由以上可以看出,第 n 个月兔子的对数为
 f(n) = f(n - 1) + f(n - 2)
 f(n-1) 是上个月的兔子数量,是原来有的
 f(n-2) 是可以生育的兔子数,即多出来的数量

第 n-2 个月开始后的第 3 个月是第 n 个月,此时第n-2个月时的兔子都可以生育了


3、整数的划分问题

参考:http://www.cnblogs.com/dolphin0520/archive/2011/04/04/2005098.html

所谓整数划分,是指把一个正整数 n 写成如下形式:

n = m1 + m2 + ... + mi (其中 mi 为正整数,并且 1 <= mi <= n),则 {m1, m2, ..., mi} 为 n 的一个划分

如果 {m1, m2, ..., mi} 中的最大值不超过 m,即 max(m1, m2, ..., mi) <= m,则称它属于 n 的一个 m 划分,这里我们记 n 的 m 划分的个数为 f(n, m)

例如当 n = 4 时,他有 5 个划分,{4}, {3, 1}, {2, 2}, {2, 1, 1}, {1, 1, 1, 1}

注意 4 = 1 + 3 和 4 = 3 + 1 被认为是同一个划分

该问题是求出 n 的所有划分个数,即 f(n, n)

下面我们考虑求 f(n, m)的方法


递归法:

根据 n 和 m 的关系,考虑以下几种情况:

(1)当 n = 1 时,不论 m 的值为多少(m > 0),只有一种划分即 {1}

(2)当 m = 1 时,不论 n 的值为多少,只有一种划分即 n 个 1,{1, 1, 1, ..., 1}

(3)当 n = m 时,根据划分中是否包含 n,可以分为两种情况:

     (a)划分中包含n的情况,只有一个即 {n}

     (b)划分中不包含 n 的情况,这时划分中最大的数字也一定比 n 小,即 n 的所有 (n-1) 划分

因此 f(n, n) = 1 + f(n, n-1)

(4)当 n < m 时,由于划分中不可能出现负数,因此就相当于 f(n, n)

(5)当 n > m时,根据划分中是否包含最大值 m,可以分为两种情况:

       (a)划分中包含m的情况,即 {m, {x1, x2, ... xi}}, 其中 {x1, x2, ...  xi} 的和为 n-m,因此这情况下为 f(n-m, m)

       (b)划分中不包含 m 的情况,则划分中所有值都比 m 小,即 n 的 (m-1) 划分,个数为 f(n, m-1)
因此 f(n, m) = f(n-m, m) + f(n, m-1)


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;

const int mod = 1e9 + 7;
const int INF = 0x7fffffff;
int n;

int Divide(int n, int m);

int main()
{
    scanf("%d", &n);
    printf("%d\n", Divide(n, n));
    return 0;
}

int Divide(int n, int m)
{
    if (m == 1 || n == 1) {
        return 1;
    }
    if (n < m) {
        return Divide(n, n);
    }
    if (n == m) {
        return 1 + Divide(n, n-1);
    }
    return Divide(n-m, m) + Divide(n, m-1);
}


4、全排列


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;

const int mod = 1e9 + 7;
const int INF = 0x7fffffff;
const int maxn = 1e6 + 10;
int n;
int num[maxn];
int Count = 0;

void Full_Array(int k);

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &num[i]);
    }
    Full_Array(0);
    return 0;
}

void Full_Array(int k)
{
    if (k == n-1) {
        ++Count;
        for (int i = 0; i < n; ++i) {
            printf("%d ", num[i]);
        }
        printf("\n");
    } else {
        for (int i = k; i < n; ++i) {
            swap(num[i], num[k]);
            Full_Array(k+1);
            swap(num[i], num[k]);
        }
    }
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;

const int mod = 1e9 + 7;
const int INF = 0x7fffffff;
const int maxn = 1e6 + 10;
int n;
int num[maxn];

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &num[i]);
    }
    do {
        for (int i = 0; i < n; ++i) {
            printf("%d ", num[i]);
        }
        printf("\n");
    } while (next_permutation(num, num+n));
    return 0;
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;

const int mod = 1e9 + 7;
const int INF = 0x7fffffff;
const int maxn = 1e6 + 10;
int n;
int num[maxn];
int Count = 0;
bool vis[maxn];

void Full_Array(int k);

int main()
{
    scanf("%d", &n);
    memset(vis, false, sizeof(vis));
    Full_Array(1);
    return 0;
}

void Full_Array(int k)
{
    if (k == n+1) {
        ++Count;
        for (int i = 1; i <= n; ++i) {
            printf("%d ", num[i]);
        }
        printf("\n");
        return;
    } else {
        for (int i = 1; i <= n; ++i) {
            if (!vis[i]) {
                num[k] = i;
                vis[i] = true;
                Full_Array(k+1);
                vis[i] = false;
            }
        }
    }
}


5、八皇后问题

19 世纪著名的数学家高斯 1850 年提出:

在 8 * 8 格的国际象棋上摆放八个皇后,使其不能互相攻击

即任意两个皇后都不能处于同一行,同一列或同一斜线上


问有多少种解法


正确的结果为92

参考:http://blog.163.com/yichangjun1989@126/blog/static/13197202820145226051666/

由于皇后们是不能放在同一行的,所以我们可以去掉 “行” 这个因素
即第1次考虑把皇后放在第1行的某个位置
第2次考虑把皇后放在第2行的某个位置
第3次考虑把皇后放在第3行的某个位置, 这样依次去递归

每计算1行,递归一次,每次递归里面考虑8列,即对每一行皇后有8个可能的位置可以放

找到一个与前面行的皇后都不会互相攻击的位置, 然后再递归进入下一行
找到一组可行解即可输出,然后程序回溯去找下一组可靠解

我们用一个一维数组来表示相应行对应的列,比如 Column[i] = j表示,第 i 行的皇后放在第 j 列

如果当前行是 r,皇后放在哪一列呢?Column[r] 列
一共有8列,所以我们要让 Column[r] 依次取第 1 列,第 2 列,第 3 列……一直到第 8 列

每取一次我们就去考虑,皇后放的位置会不会和前面已经放了的皇后有冲突
怎样是有冲突呢?

同行,同列,对角线

由于已经不会同行了,所以不用考虑这一点

同列:Column[r] == Column[j]

同对角线有两种可能,即主对角线方向和副对角线方向

主对角线方向满足,行之差等于列之差:r - j == Column[r] - Column[j]
副对角线方向满足, 行之差等于列之差的相反数:r - j == Column[j] - Column[r]
只有满足了当前皇后和前面所有的皇后都不会互相攻击的时候,才能进入下一级递归


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;

const int mod = 1e9 + 7;
const int INF = 0x7fffffff;
const int maxn = 10 + 10;
int Column[maxn];
int Count = 0;

void Eight_Queen(int row);

int main()
{
#ifdef __AiR_H
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif // __AiR_H
    Eight_Queen(1);
    printf("%d\n", Count);
    return 0;
}

void Eight_Queen(int row)
{
    if (row > 8) {
        ++Count;
        for (int i = 1; i <= 8; ++i) {
            for (int j = 1; j <= 8; ++j) {
                if (Column[i] == j) {
                    printf("@ ");
                } else {
                    printf(". ");
                }
            }
            printf("\n");
        }
        printf("\n");
        return;
    }
    for (int i = 1; i <= 8; ++i) {
        Column[row] = i;
        bool ok = true;
        for (int j = 1; j < row; ++j) {
            if (Column[j] == i || row - j == abs(Column[row] - Column[j])) {
                ok = false;
                break;
            }
        }
        if (ok) {
            Eight_Queen(row + 1);
        }
    }
}










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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值