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);
}
}