题目
求三柱汉诺塔有N层的时候,打印最优移动过程和最优移动总步数。且限制不能从最左侧的塔直接移动到最右侧,也不能从最右侧直接移动到最左侧,而是必须经过中间。
思路
修改后的汉诺塔问题实际动作只有4个:“左”到“中”,“中”到“左”,“中”到“右”,“右”到“中”。
一个动作能发生的先决条件有两个:
- 不违反小压大原则:from栈弹出的元素num如果想压入to栈中,那么num的值必须小于当前to栈的栈顶。
- 相邻不可逆原则:如果想走出最少步数,那么任何两个相邻的动作都不是互为逆过程的。
根据以上两个原则,可以推导出两个结论:
- 第一个动作一定是L->M。
- 在走出最少步数过程中的任何时刻,四个动作只有一个动作不违反以上两个原则,另外三个动作一定都会违反。
代码实现
//三柱汉诺塔,且规定每次只能在相邻柱之间移动
public class HanoiProblem {
public enum Action {
No, LToM, MToL, MToR, RToM
}
public int hanoiProblem(int num, String left, String mid, String right) {
Stack<Integer> lS = new Stack<>();
Stack<Integer> mS = new Stack<>();
Stack<Integer> rS = new Stack<>();
lS.push(Integer.MAX_VALUE);
mS.push(Integer.MAX_VALUE);
rS.push(Integer.MAX_VALUE);
for (int i = num; i > 0; i--) {
lS.push(i);
}
Action[] record = {Action.No};
int step = 0;
while (rS.size() != num + 1) {
step += fStackTotStack(record, Action.MToL, Action.LToM, lS, mS, left, mid);
step += fStackTotStack(record, Action.LToM, Action.MToL, mS, lS, mid, left);
step += fStackTotStack(record, Action.RToM, Action.MToR, mS, rS, mid, right);
step += fStackTotStack(record, Action.MToR, Action.RToM, rS, mS, right, mid);
}
return step;
}
public static int fStackTotStack(Action[] record, Action preNoAct, Action nowAct, Stack<Integer> fStack, Stack<Integer> tStack, String from, String to) {
if (record[0] != preNoAct && fStack.peek() < tStack.peek()) {
tStack.push(fStack.pop());
System.out.println("Move " + tStack.peek() + " from " + from + " to " + to);
record[0] = nowAct;
return 1;
}
return 0;
}
public static void main(String[] args) {
HanoiProblem hanoi = new HanoiProblem();
System.out.println("求解三层汉诺塔最小步数:" + hanoi.hanoiProblem(3, "left", "mid", "right"));
}
}