Description
Hanoi Tower is a famous game invented by the French mathematician Edourard Lucas in 1883. We are given a tower of n disks, initially stacked in decreasing size on one of three pegs. The objective is to transfer the entire tower to one of the other pegs, moving only one disk at a time and never moving a larger one onto a smaller.
The best way to tackle this problem is well known: We first transfer the n-1 smallest to a different peg (by recursion), then move the largest, and finally transfer the n-1 smallest back onto the largest. For example, Fig 1 shows the steps of moving 3 disks from peg 1 to peg 3.
Now we can get a sequence which consists of the red numbers of Fig 1: 1, 2, 1, 3, 1, 2, 1. The ith element of the sequence means the label of the disk that is moved in the ith step. When n = 4, we get a longer sequence: 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1. Obviously, the larger n is, the longer this sequence will be.
Given an integer p, your task is to find out the pth element of this sequence.
Input
The first line of the input file is T, the number of test cases.
Each test case contains one integer p (1<=p<10^100).
Output
Output the pth element of the sequence in a single line. See the sample for the output format.
Print a blank line between the test cases.
题目解析:这道题主要是求解汉诺塔第p个数的值是多少。现在我们先来看一下汉诺塔的序列关系:
1:1
2:121
3:1213121
4:121312141213121
其实细心可以发现,对于1这个数,都是隔1个元素出现的,对于2这个数,是隔4(也就是2²)个元素,对于3这个数,是隔着8(也就是2³)个元素出现的.....
可以知道对于n这个数,是隔着n^2个元素出现的。对于1这个数,偏移量为1(2^0),对于2这个数,偏移量为2(2^1)......所谓的偏移量就是数字n第一次出现的位置。
因此对于一个数a,它的位置标号可以这样表示: a的位置标号 = a的间隔*n +a的偏移量。其中n表示整数。因此有:
数字 | 偏移量 | 间隔 |
1 | 2^0 | 2^1 |
2 | 2^1 | 2^2 |
3 | 2^2 | 2^3 |
n | 2^(n-1) | 2^n |
因此4这个数的位置标号为:8,24,40...
因此第n个数的位置标号为 2^(n-1) + k * (2^n) k = 0, 1, 2, ...
现在的问题是,知道了位置标号m,求对应的数字n是多少。因此有 m = 2^(n-1) + k *2^n
解题思路: 用m除以2,看能够整除多少次,能够整除的次数 + 1就是所求的数。这是因为:
[ 2^(n-1) + k * (2^n)] / (2^(n-1)) = 1 + 2*k 在这里m除以2 除了(n-1)次,再下一次是(1+2*k)除以2肯定不能整除。
因此所求的数便是m所能整除2的次数 +1
在这道题还需注意的是输入位置标号数的范围,防止溢出,这里使用了处理字符串的方式处理
备注:题目要求每个case之间要有一行空白。因为每次结果输出都要输出一个空白行 cout << endl;
#include <iostream> #include <map> using namespace std; pair<string, int> Mod(string strInt){ int dividend = 0; string result; for (int i = 0; i < strInt.size(); i++) { // 用于处理长整数 dividend = dividend* 10 + (strInt[i] - '0'); int tmp = dividend / 2; dividend = dividend %2; result = result + (char)(tmp + '0'); } while (1) { if (result.size() == 1)break; if (result[0] == '0') result.erase(result.begin()); else break; } return make_pair(result,dividend); } int main(int argc, const char * argv[]) { // insert code here... int T; cin >> T; for (int i = 1; i <= T; i++) { string data; cin >> data; cout << "Case " << i << ": " ; int count = 0; while (1) { pair<string, int> result = Mod(data); // 整除2 ,结果返回商和余数 data = result.first; if (result.second == 0) count ++; // 余数为0说明可以整除 else break; } cout <<count + 1 << endl; if (i != T) { cout << endl; } } return 0; }