借助堆栈以非递归(循环)方式求解汉诺塔的问题(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<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]);
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<iostream>
#include <stack>
char s[4] = { 'q','a','b','c' };
std::stack<int> 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.让人看得懂 > 代码简短