题目大意:基于汉诺塔原型,第一根柱子上有n个盘子,从上至下编号从1依次递增至n。在最佳移动方案中,第m次所移动的盘子的编号。
解题思路:模拟必然是会超时的。但根据汉诺塔的递归原理,容易发现,对于n阶汉诺塔,将第一个盘从A柱移动到B柱是一步,将前两个盘从A柱移动到B柱是3步,以此类推,将n个盘从A柱移动到B柱的步数是2^n-1步。而第m步必然在以上递推的值所划分出来的区间之中。查找到区间i后,可以发现,我们把问题缩小为求n-i阶汉诺塔的第m-(used[i]+1)步。同时,如果发现第m步正好是i阶汉诺塔移动后的下一步,那必然是移动i+1号盘子,若正好是i阶汉诺塔移动的步数,那就必然是1号盘子,这就是递归的边界了。
每一阶所需的步数可以用公式快速得出并预缓存,相对于模拟,这种区间查找,缩小范围的方法极大地降低了时间复杂度。
1 #include <iostream> 2 using namespace std; 3 long long int cache[66]; 4 int flag=1; 5 void find(long long int m) 6 { 7 int i; 8 for(i=1;i<=65;i++) 9 { 10 if(cache[i]==m) 11 { 12 flag=1; 13 return; 14 } 15 if(cache[i]<m&&m<cache[i+1]) 16 { 17 if((cache[i]+1)==m) 18 { 19 flag=i+1; 20 return; 21 } 22 else 23 { 24 find(m-(cache[i]+1)); 25 } 26 } 27 } 28 } 29 int main() { 30 int i; 31 cache[1]=1; 32 for(i=2;i<=65;i++) 33 { 34 cache[i]=cache[i-1]*2+1; 35 } 36 long long int n,m; 37 while(cin>>n>>m) 38 { 39 if(n==0&&m==0) 40 break; 41 flag=1; 42 find(m); 43 cout<<flag<<endl; 44 } 45 return 0; 46 }
时间: 2024-10-11 09:11:49