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

来源:互联网 发布:经济学数据库 编辑:程序博客网 时间:2024/05/16 04:42

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

输入格式:

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

输出格式:

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

输入样例:

3

输出样例:

a -> ca -> bc -> ba -> cb -> ab -> c

a -> c

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

正确的解法是我查了网上的一个学者的话才得出来。

#include<iostream>#include<algorithm>#include <stack>#include <cstdio>#include<cmath>using namespace std;int temp1=-1, temp2=-1;char s[4] = { 'q','a','b','c' };//为了解题简便,我是从1开始算的stack<int> 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]);elsemove1(rank1[3], rank1[1]);}elsemove1(rank1[2], rank1[1]);tt = rank1[2]; }}


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

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

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

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

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

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


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

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

#include<iostream>#include <stack>using namespace std;char s[4] = { 'q','a','b','c' };stack<int> a[4];bool move1(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]);return true;}int main(){int  N,count = 0;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) {move1((count - 1) % 3 + 1, (count) % 3 + 1);if (!move1((count -1) % 3 + 1, (count+1) % 3 + 1))if(!move1((count +1) % 3 + 1, (count-1) % 3 + 1))break;}}


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

感想:

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

2.printf快于cout

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

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

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

                                             
0 0
原创粉丝点击