UVA 10795 A Different Task

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);
	}
}
原文地址:https://www.cnblogs.com/sahdsg/p/12343484.html