汉诺塔问题,最经典的解法是递归,但是这道题的数据太大,没有办法实现。实际上这道题要求的是找出某一位置的汉诺块,没有必要完全模拟,而是要找出特定的汉诺塔会出现在哪些位置。
观察移动的汉诺塔块的序列,可以发现n第一次出现的序列号为2^(n-1)次方。这可以由汉诺塔的归纳办法归纳证明(递归式为f(n)=f(n-1), n, f(n-1),证明略)。又由递归式可以知道,汉诺再次出现是f(n+1)等式右边两个f(n)的后一个,递归示意图如下:
可由对称性求出n第二次出现的序列号为(2^(n+1)-1)-(2^(n-1)-1)=3*2^(n-1);
再考虑f(n+2),可求出第三次,第四次出现为5*2^(n-1), 7*2^(n-1),依此类推,可知n出现的位置为2^(n-1)次方的奇数倍,这在二进制中表现为倒数第n位为第一个1的所有数。于是该题转化为高精度二进制转化的问题。
#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;
}
// by wbchou
// Feb 17th, 2012