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);
}
}
}