Sicily 1028. Hanoi Tower Sequence

题目:

Constraints

Time Limit: 1 secs, Memory Limit: 32 MB

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.

Sample Input

4
1
4
100
100000000000000

Sample Output

Case 1: 1
 
Case 2: 3
 
Case 3: 3
 
Case 4: 15

Problem Source

ZSUACM Team Member

思路:

通过观察序列可发现是中心对称的,去掉中心点分成左右两部分后着两部分也是中心对称的。

而且,不难发现,当p是2^k(k=0,1...)时,输出k+1。

所以求解过程可变为

当p是2^k(k=0,1...)时,输出k+1。如果不是,那么找到最接近p的比p小的一个2的整数次幂(2^k1),然后得到p与(2^k1)之差。这时分两种情况:

情况1:如果差也是2的整数次幂(2^k2),则输出k2+1;

情况2:如果差不是2的整数次幂,则以差作为新的p,继续寻找最接近p的比p小的一个2的整数次幂,再做差判断,然后判断是1还是2。
所以解题思路变为在对应二进制中求倒数第n位为第一个1的那个n。于是该题转化为高精度二进制转化的问题。

代码:

#include <iostream>
#include <string>

using namespace std;

pair<string, int> divide(string dividend, int divisor) {
    string quotient;
    int remainder=0;
    for (string::iterator iter=dividend.begin(); iter!=dividend.end(); iter++) {
        int tmp=remainder*10+((*iter)-'0');
        quotient+=(tmp/divisor+'0');
        remainder=tmp%divisor;
    }
    return make_pair(quotient, remainder);
}

string decimal2InverseBinary(string d) {
    string b;

    while(!d.empty()) {
        b+=(divide(d, 2).second+'0');
        d=divide(d, 2).first;
        if(d[0]=='0')
            d.erase(d.begin());
    }

    return b;
}

int main() {
    int cases;
    cin>>cases;
    int counter=0;
    while(counter++<cases) {
        string p;
        cin>>p;
        int n=decimal2InverseBinary(p).find('1')+1;
        cout<<"Case "<<counter<<": "<<n<<"\n";
        if (counter!=cases)
            cout<<"\n";
    }

    return 0;
}
当然,也可以不进行二进制转化,细想一下,求倒数第n位为第一个1,其实与1后面那些0的个数有关,二进制中每除一次2向右移动1位,所以只要求可以被2整除的次数然后加上1就可以了。

代码:

#include <iostream>
#include <string>
using namespace std;

int getMode(string a,int b)
{
    int temp = 0;
    for(int i = 0;i < a.length();i++)
    {
        int t = temp * 10 + (a[i] - '0');
        temp = t % b;
    }

    return temp;
}

string div(string a,int b)
{
    string result = "";

    int temp = 0;
    for(int i = 0;i < a.length();i++)
    {
        int t = temp * 10 + (a[i] - '0');
        temp = t % b;

        result += (t / b + '0');
    }

    return result;
}

int cal(string s)
{
    int count = 1;

    while(1)
    {
        if(getMode(s,2) == 0)
        {
            count++;
            s = div(s,2);
        }
        else
        {
            return count;
        }
    }
}

int main()
{
    int cases;
    int caseid = 0;
    cin >> cases;

    int count = cases;
    while(cases--)
    {
        caseid++;
        string s;
        cin >> s;

        if(caseid == count)
        {
            cout << "Case " << caseid << ": " << cal(s) << endl;
        }
        else
        {
            cout << "Case " << caseid << ": " << cal(s) << endl;
            cout << endl;
        }

    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值