<OJ_Sicily>Hanoi_Tower_Sequence

40 篇文章 0 订阅

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表示整数。因此有:

数字偏移量间隔
12^02^1
22^12^2
32^22^3
n2^(n-1)2^n
举个例子,数字4,其所在的位置标号 = 2^3 + 2^4 * k (k=0,1,2...)

因此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;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值