现在,换一种方式来看hanoi塔问题。这次用模板。来看这段代码:
#include <iostream>
using namespace std;
template <int n, int a, int b, int c>
class Tower {
public:
Tower() {
Tower<n-1, a, c, b> t1;
cout<<"move plate"<<n<<" from "<<"ABC"[a]<<" to "<<"ABC"[c]<<"\n";
Tower<n-1, b, a, c> t2;
}
};
template <int a, int b, int c>
class Tower<1,a,b,c> {
public:
Tower() {
cout<<"move plate"<<1<<" from "<<"ABC"[a]<<" to "<<"ABC"[c]<<"\n";
}
};
int main()
{
Tower<10, 0, 1, 2> hanoi;
}
代码是超级简单的。大于一层的hanoi塔含有两个子hanoi塔问题,它们的类型分别是Tower<n-1, a, c, b>和Tower<n-1, b, a, c>。移动盘子的操作是在构造函数里完成的。所以,定义一个hanoi变量就好了,什么都不用做。
这段代码编译时会给编译器带来什么样的开销呢?因为每个Tower实例,都用到了两个n-1层的子类型,咋一看一共生成2的n次方-1个类型。然而,根据完全2-叉树的推算公式每一层有2的i次方个节点。而每一层,按a,b,c的全排列,最多只能生成6个类型,所以没那么多。数学推导就不做了,仍然利用这个代码自动计算数一下。
#include <iostream>
using namespace std;
#define MAX 10
int count[MAX][6];
enum {
I_abc, I_acb, I_bac, I_bca, I_cab, I_cba};
#define TEST_VAL(a,b,c) (a<<8|b<<4|c)
int add(int n, int a, int b, int c)
{
extern int count[MAX][6];
switch(TEST_VAL(a,b,c)) {
case TEST_VAL(0,1,2):
if(count[n-1][I_abc]) return 0;
count[n-1][I_abc]=1;
break;
case TEST_VAL(0,2,1):
if(count[n-1][I_acb]) return 0;
count[n-1][I_acb]=1;
break;
case TEST_VAL(1,0,2):
if(count[n-1][I_bac]) return 0;
count[n-1][I_bac]=1;
break;
case TEST_VAL(1,2, 0):
if(count[n-1][I_bca]) return 0;
count[n-1][I_bca]=1;
break;
case TEST_VAL(2, 0,1):
if(count[n-1][I_cab]) return 0;
count[n-1][I_cab]=1;
break;
case TEST_VAL(2,1,0):
if(count[n-1][I_cba]) return 0;
count[n-1][I_cba]=1;
break;
}
return 1;
}
template <int n, int a, int b, int c>
class Tower {
public:
Tower() {
if (!add(n,a, b,c)) return;
Tower<n-1, a, c, b> t1;
cout<<"move plate"<<n<<" from "<<"ABC"[a]<<" to "<<"ABC"[c]<<"\n";
Tower<n-1, b, a, c> t2;
}
};
template <int a, int b, int c>
class Tower<1,a,b,c> {
public:
Tower() {
if (!add(1,a, b,c)) return;
cout<<"move plate"<<1<<" from "<<"ABC"[a]<<" to "<<"ABC"[c]<<"\n";
}
};
int main()
{
extern int count[MAX][6];
Tower<10, 0, 1, 2> hanoi;
int i, j;
for(i=0; i<10; i++) {
cout <<i+1<<":";
for(j=0; j<6; j++) {
cout <<" ";
if (count[i][j]) {
cout<< "x";
}
else cout << ".";
}
cout <<"\n";
}
}
这里count数组意外的和std名称空间的某个东西冲突了。只好在函数体里再用extern说明一下。看来using namespace std;这句话有点大了…
这是运行结果:
move plate ...
move ...
1: . x x . . x
2: x . . x x .
3: . x x . . x
4: x . . x x .
5: . x x . . x
6: x . . x x .
7: . x x . . x
8: x . . x x .
9: . x x . . .
10: x . . . . .
可见除了最后两层,每层都生成3个类型。总共是 3 *(n-2) +3 = 3 *n -3
编译器总共实例化生成 3 *n -3个Tower类型。