分析:对于递推,我一向是不擅长的,这个题看了刘老师的解法,不由惊叹。。。哎~~~差距可是大了去了
考虑编号最大的盘子,如果这个盘子的初始局面和结束局面在同一根柱子上,则不需要移动,那么我们应该找出初始与结束所在柱子不同编号最大的一个,设为k(k必须移动)。把大于k的盘子看作不存在。
假设k要从柱子1移动到柱子2,编号比k小的既不能在柱子1上也不能在柱子2上,那么只能位于柱子3上,换句话说,这时候柱子1上只有k,柱子2是空的,柱子3从上往下依次1,2,3,。。。,k-1(我们把这个局面称为参考局面)
本题最后的结果 = 初始 -> 参考 + 结束 -> 参考 + 1 (写一个函数 F(P, i, final) 表示已知初始柱子编号P数组, 把1, 2, 3, 。。。,i 全部移动到final 所需步数) 即 F(start, k-1, 6-start[k] - finish[k]) + F(finish, k-1, 6-start[k] - finish[k])
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
LL f(int *P, int i, int Final) //计算f(P, i, final)
{
if (i == 0) return 0;
if (P[i] == Final) return f(P, i-1, Final);
else return f(P, i-1, 6-P[i]-Final) + (1LL << (i-1));//需要用6-P[i] -final作中转,汉诺塔结论
}
int start[65], finish[65], n;
int main()
{
int kase = 0;
while (scanf("%d", &n) && n){
for (int i = 1; i<=n; i++) scanf("%d", &start[i]);
for (int i = 1; i<=n; i++) scanf("%d", &finish[i]);
int k = n;
while (k>=1 && start[k] == finish[k]) k--; //寻找k
LL ans = 0;
if (k>=1){
int other = 6 - start[k] - finish[k];
ans = f(start, k-1, other) + f(finish, k-1, other) + 1;
}
printf("Case %d: %lld\n", ++kase, ans);
}
return 0;
}