5-17 汉诺塔的非递归实现 (25分)

借助堆栈以非递归(循环)方式求解汉诺塔的问题(n, a, b, c),即将N个盘子从起始柱(标记为“a”)通过借助柱(标记为“b”)移动到目标柱(标记为“c”),并保证每个移动符合汉诺塔问题的要求。

输入格式:

输入为一个正整数N,即起始柱上的盘数。

输出格式:

每个操作(移动)占一行,按柱1 -> 柱2的格式输出。

输入样例:

3

输出样例:

a -> c
a -> b
c -> b
a -> c
b -> a
b -> c

a -> c

以下是我当初的做法,我认为已经简便到不能再简便了,基本上每次的循环里只有一个3个数的排序,5个左右的大小判断然后得出结果,最后的一个测试点却还是超时。

正确的解法是我查了网上的一个学者的话才得出来。以下是我原先超时的解法:

#include
#include
#include 
#include 
#include
using namespace std;
int temp1=-1, temp2=-1;
char s[4] = { 'q','a','b','c' };//为了解题简便,我是从1开始算的
stack a[4];
int c1 = 0;
int rank1[4];
bool compare(int a1,int b1) { //给栈顶元素排序用
	if (a[a1].top() >= a[b1].top())
		return true;
	if (a[a1].top() < a[b1].top())
		return false;
	return false;
}
bool move1(int before,int after) {   //移动物块
	if ((a[after].top() - a[before].top())%2==0)
		return false;
	a[after].push(a[before].top());
	a[before].pop();
	temp1 = before; temp2 = after;  //记录上一次移动的物块位置
	printf("%c -> %c\n",s[temp1],s[temp2]);//printf比cout要快
	c1++;
	return true;
}
int main(){
	int i, N;
	cin >> N;
	a[1].push(N+1);          //保证栈不会为空
	for (i = 0; i < N; i++)
		a[1].push(N-i);   //初始化
	a[2].push(N + 2);
	a[3].push(N+3);
	if (N % 2 == 1) {        //N为奇数还是偶数,第一次移物块到哪里是不同的
		move1(1, 3);
		temp1 = 1;
		temp2 = 3;
	}
	else {
		move1(1, 2);
		temp1 = 1; temp2 = 2;
	}
	for (i = 1; i <= 3; i++)//初始化栈排序表
		rank1[i] = i;
	int tt;
	while (c1 < pow(2, N) -1) {
		sort(rank1 + 1, rank1 + 4, compare);//按compare函数排序
			if (temp2 == rank1[2]) { //刚移动过的物块不会再被移动
				if(tt==temp1)   //别问我为什么,找规律找出来的
					move1(rank1[3], rank1[2]);
				else
					move1(rank1[3], rank1[1]);
			}
			else
				move1(rank1[2], rank1[1]);
		tt = rank1[2]; 
	}
}

日了狗了。。。。我搞了很久各种优化还是不行,直到去网上看到一段话:

一个美国学者总结得到:所有的汉诺塔移动可以总结为重复的两步,我们假设现在最小的圆盘在a柱子上,柱子为a,b,c

第一步:将最小圆盘移动到下一个柱子上,也就是b

第二步:对a柱子和c柱子进行顶上最小的元素进行判断,把小一点的那个圆盘移动到大一点的那个圆盘(有空则摞在空柱子上)。

重复上述两步就可以得到答案。

注意:这样得到的最后的答案不一定是摞在c上,如果N是偶数将摞在b上,所以如果N是偶数我们就令第二个柱子为c,第三个柱子为b,这样就一定最后是摞在c上的。

 

我试了一下沃日真的可以TAT

下面是源代码,非常地好理解,和上面学者的话的思路一模一样,我就不注释了:

#include
#include 
char s[4] = { 'q','a','b','c' };
std::stack a[4];
bool move(int before, int after) {
	if (a[before].empty())
		return false;
	if (!a[after].empty())
		if ((a[after].top() - a[before].top()) < 0)
			return false;
	a[after].push(a[before].top());
	a[before].pop();
	printf("%c -> %c\n", s[before], s[after]);//faster than cout
	return true;
}
int main() {
	int  N, count = 0;
	std::cin >> N;
	for (int i = 0; i < N; i++)
		a[1].push(N - i);
	if (N % 2 == 1) {
		s[2] = 'c'; s[3] = 'b';
	}
	while (++count) {
		move((count - 1) % 3 + 1, (count) % 3 + 1);
		if (!move((count - 1) % 3 + 1, (count + 1) % 3 + 1)&&!move((count + 1) % 3 + 1, (count - 1) % 3 + 1))
				break;
	}
}

如果不去网上查,这题不知道要坑我多少时间。。。。

感想:

1.能用人脑能完成的部分尽量不要让计算机去完成,这样可以节省不少时间

2.printf快于cout

3.做题不要死磕,有些题是有毒的

4.不要尝试去自己写stack或者queue,deque来代替STL里的容器,这并不能节省多少时间

5.让人看得懂 > 代码简短

你可能感兴趣的:(5-17 汉诺塔的非递归实现 (25分))