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
// Problem#: 1028
// Submission#: 2346268
// The source code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
// URI: http://creativecommons.org/licenses/by-nc-sa/3.0/
// All Copyright reserved by Informatic Lab of Sun Yat-sen University
#include<iostream>
#include<cmath>
#include<string>
using namespace std; bool diff(string a,string b){
int lena=a.size();
int lenb=b.size();
if(lena<lenb)
return true;
else if(lena == lenb){
int i;
for(i=0;i<lena;i++){
if(a[i]<b[i]){
return true;
}
else if(a[i]==b[i]){
}
else{
return false;
}
}
}
else
return false;
return false;
}
string add(string a,string b){
string temp;
int lena=a.size();
int lenb=b.size();
int x,y;
x=lena;
y=lenb;
if(x>y){
temp='0'+a;
int i=0;
int lent=temp.size();
for(i=1;i<=lenb;i++){
temp[lent-i-1]=((temp[lent-i]-'0')+(b[lenb-i]-'0'))/10+temp[lent-i-1];
temp[lent-i]=((temp[lent-i]-'0')+(b[lenb-i]-'0'))%10+'0'; }
}
else{
temp='0'+b;
int lent=temp.size();
int i=0;
for(i=1;i<=lena;i++){
temp[lent-i-1]=((a[lena-i]-'0')+(temp[lent-i]-'0'))/10+temp[lent-i-1];
temp[lent-i]=((a[lena-i]-'0')+(temp[lent-i]-'0'))%10+'0'; }
}
int lent=temp.size();
int i=0;
while(temp[i]=='0'){
i++;
}
temp.erase(temp.begin(),temp.begin()+i);
return temp;
} string pluss(string a,string b){
string temp;
int lena=a.size();
int lenb=b.size();
int x=lena,y=lenb;
int key;
for(key=1;key<=y;key++){
if(a[lena-key]>=b[lenb-key]){
temp=(char)(a[lena-key]-b[lenb-key]+'0')+temp;
}
else if(a[lena-key-1]!='0'){
a[lena-key-1]=a[lena-key-1]-1;
temp=(char)(a[lena-key]-b[lenb-key]+'0'+10)+temp;
}
else{
int i=1;
while(a[lena-key-i]=='0'){
i++;
}
a[lena-key-i]=a[lena-key-i]-1;
i--;
while(i>=1){
a[lena-key-i]='9';
i--;
}
temp=(char)(a[lena-key]-b[lenb-key]+'0'+10)+temp;
}
}
int lent=temp.size();
int i=0;
while(temp[i]=='0'){
i++;
}
temp.erase(temp.begin(),temp.begin()+i);
return temp;
} int getCase(string p){
int i=0;
string temp;
temp=temp+'1';
string front;
while(diff(temp,p)){
front=temp;
temp=add(temp,temp);
i++;
}
if(temp==p){
return i+1;
}
else{
p=pluss(p,front);
return getCase(p);
}
} int main(){
int n;
cin>>n;
int i=0;
while(n--){
string p;
i++;
cin>>p;
if(n==0){
cout<<"Case "<<i<<": "<<getCase(p)<<endl;
}
else{
cout<<"Case "<<i<<": "<<getCase(p)<<endl<<endl;
}
}
return 0;
}
首先是要找规律,然后是大精度加减法以及比较大小的问题,大精度的那些函数是用string实现的。
至于规律,我们可以简单的写下钱16次操作,我们会发现,当操作次数为2的(0,1,2,3....)次方的时候,对应操作的disk序号一定是第一次出现(1,2,3,4...),而且为操作次数加1,另外你会发现操作数为2的(0,1,2,3....)次方的时候下一位一定从disk1开始重复前面的所有步。
所以我们可以找到离操作数最近的那个2的幂,然后把操作数减去2的幂得到的值赋值给操作数,再用递归的方法求出答案。方法仅供参考。