https://vjudge.net/problem/UVA-10795
题目
输入两个状态,把汉诺塔从一个状态一直移动到另外一个状态最少需要几步?
例如从左图到右图需要最少6步
题解
首先考虑最大的圆盘(k)如何移动到目标柱子上。如果就在目标柱子就不需要移动,考虑下一个最大的(k=k-1)。
肯定是把小于它的圆盘(<k)移动到对于最大的圆盘(k)来说非起始并且是非目标的柱子中(1+2+3-s-t),然后把最大的圆盘(k)移动到目标柱子中。
这一步是必须经过的,而且没有其他的走法。
那么可以在起始状态移动一次最大的,目标状态移动一次最大的,这样就会到达同一状态。
现在的问题是如何把编号为0~n的圆盘移动到一个指定的柱子中。
只有一个圆盘的时候,直接判断
仍然考虑最大的,如果本身就在指定的柱子,n--;
否则,先把0~n-1的圆盘移动到起始柱子和指定的柱子以外的另外一个柱子,然后把n移动到指定的柱子,最后把0~n-1移动到指定的柱子,可以推公式得到最后这一步需要$2^{n-1}-1$步
AC代码
#include<cstdio> #include<cstring> #include<algorithm> #include<cctype> #include<unordered_map> #include<string> #define REP(i,a,b) for(register int i=(a); i<(b); i++) #define REPE(i,a,b) for(register int i=(a); i<=(b); i++) #define PERE(i,a,b) for(register int i=(a); i>=(b); i--) using namespace std; typedef long long ll; #define MAXN 67 int st[MAXN], ed[MAXN]; ll calc(int *P, int k, int j) { if(k==-1) return 0; if(P[k]==j) return calc(P,k-1,j); return calc(P,k-1,6-P[k]-j)+(1ll<<(k+1-1)); } int main() { int n; int kase=0; while(~scanf("%d", &n) && n) { REP(i,0,n) scanf("%d", &st[i]); REP(i,0,n) scanf("%d", &ed[i]); ll ans=0; PERE(i,n-1,0) if(st[i]!=ed[i]) { int other = 6-st[i]-ed[i]; ans = calc(st, i-1, other) + calc(ed, i-1, other) + 1; break; } printf("Case %d: %lld ", ++kase, ans); } }