uva 10795 A Different Task
https://vjudge.net/problem/UVA-10795
题意:
旧的汉诺塔问题的改版,原来是叫你从一根柱子移到另一根柱子,现在是给出合理的起始局面和目标局面,计算最少移动的步数。
思路:
参考刘汝佳大大的训练指南。
首先,我们可以确定的是如果一个编号最大的圆盘在起始局面和目标局面中的位置相同的话,那么它肯定不需要移动,所以我们只考虑编号最大设为k并且它最后的柱子和开始的柱子不一样的圆盘。
然后,盘子的移动是可逆的,因为它每一次的移动都必须满足条件,即从面积比它大的圆盘或者空柱子上移动到面积比它大的圆盘或者空柱子上,所以每一次的移动是可逆的。
现在,考虑k这个圆盘。假设现在将要移动k,从柱子1移动到柱子2,那么编号比k大的忽略掉,编号比k小的既不能在1上也不能在2上,只能在3上。这是1上只有柱子k,2为空,柱子3上是从1到k - 1的圆盘。这个局面叫做参考局面。
因为盘子移动的可逆性,根据对称性可知,只需要求出初始局面到参考局面的步数加目标局面到参考局面的步数,再加上k圆盘从起始柱子到目标柱子的1步,就是答案。
现在,编写一个函数f(p,i,end)表示各盘子的初始编号的数组为p,把盘子1,2,3,……,i全部移动en柱子上的所需的步数,那么答案就出来了:
f(start,k - 1,6 - start[k] - end[k]) + f(end,k-1,6 - start[k]-end[k]) + 1。6 - start[k]-end[k]指的参考局面中放置1到k-1的圆盘的柱子(即为中转柱子)。
之后需要计算f(p,i,end),那么如果说i在end柱子上,那么f(p,i,end) = f(p,i-1,end);
如果不在的话,那么就需要把前i - 1个圆盘移动到中转的柱子上,然后把i移到end上,再把i-1个圆盘移回来。这个操作所需的步数按照旧的汉诺塔的结论是2^(i-1) - 1,那么加上把i移动到end的操作,就是2^(i-1),即为当p[i] != end,f(p,i,end) = f(p,i-1,end) + 2^(i-1)。
:)
代码:
1 #include <stdio.h> 2 3 int a[70],b[70]; 4 5 long long f(int *p,int i,int en) 6 { 7 if (i == 0) return 0; 8 if (p[i] == en) return f(p,i - 1,en); 9 return f(p,i-1,6 - p[i] - en) + ((long long) 1 << (i - 1)); 10 } 11 12 int main() 13 { 14 int n; 15 16 int cas = 0; 17 18 while (scanf("%d",&n) == 1 && n) 19 { 20 long long ans = 0; 21 22 for (int i = 1;i <= n;i++) scanf("%d",&a[i]); 23 for (int i = 1;i <= n;i++) scanf("%d",&b[i]); 24 25 int k = n; 26 27 while (a[k] == b[k]) k--; 28 29 if (k >= 1) 30 { 31 ans = 1 + f(a,k-1,6 - a[k] - b[k]) + f(b,k-1,6 - a[k] - b[k]); 32 33 printf("Case %d: %lld\n",++cas,ans); 34 } 35 else printf("Case %d: %d\n",++cas,0); 36 } 37 38 return 0; 39 }