Sicily 解题: 1028 Hanoi Tower Sequence

原创 2007年09月11日 15:48:00
   

Hanoi Tower Sequence


Total Submit : 841    Accepted Submit : 212    

Problem

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 

==================================我是华丽的分割线==============================

 分析:

序列的第 2 k - 1 + i * 2  k  = t * 2k - 1 个数是 k ,i = 1, 2, 3, ... ,t = 1, 3, 5, ...
因此对于输入的正整数 n ,只要计算 n 能被 2 整除的次数,然后输出次数加 1 即可。
使用 109 进制的大整数来表示 n , 可以提高效率。

// By Rappizit@2007-09-11

#include 
<cstdio>
#include 
<cstring>
using namespace std;

const int DIGIT = 9;        // 大整数的进制为 10^DIGIT(大整数每个单元相当 DIGIT 个十进制数字)
const long long BitBase = 10;
const long long UnitBase = 1000000000;
const long long BASE [10= ...{110100100010000100000
            
1000000100000001000000001000000000}
;
const int SHBITS = 18;
const long long POW2 = 262144;    //1 << 18;
const long long mask = 262143;    //(1 << 18) - 1;

const int N = 101;                // 输入的最大位数
char str [N + 1];                // 输入的字符串存放于 str []
long long vli [N / DIGIT + 1];        // 字符串转换成 10^9 进制的大整数的各个单元存放于 vli []

int len = 0;                // 输入的字符串长度(十进制大整数位数)
int unit = 0;                // 转成 10^9 进制的大整数的单元数

// 假设字符串 str 正确表示一个正整数,将该字符串转换成 10^9 进制的大整数,并存放到 vli []
void str2vli ()
...{
    
int size = len;
    unit 
= (size + DIGIT - 1/ DIGIT;    // 大整数的单元数为 ceil (double (size) / DIGIT)
    long long cx = 0;
    
int p = 0;                // 当前单元的下标
    int i, j, k;

    
// 从字符串的最末位置开始,每 DIGIT 个字符作为一个 UNIT,最后可能剩下几个字符
    for (i = size - 1, j = DIGIT - 1; i >= j; i -= DIGIT)
        
...{
        cx 
= 0;
        
for (k = 0; k < DIGIT; k ++)
            
...{
            cx 
+= BASE [k]  * (long long)(str [i - k] - '0');
            }

        vli [p 
++= cx;
        }


    
if (p < unit)
        
...{
        
// 处理剩下的几个字符
        for (k = 0, cx = 0; i >= 0; i --, k ++)
            
...{
            cx 
+= BASE [k] * (long long)(str [i] - '0');
            }

        vli [p 
++= cx;
        }

}


// 假设大整数 vli > 0,计算 vli 能被 2 整除的次数
int times ()
...{
    
int p = 0;    // 大整数 vli 能被 2 整除的次数
    int size = unit;    // size 为 vli 的单元数

    
// 如果 size 为 1 那么直接用 2 来试除 vli,每除一次, p 增加 1
    if (size == 1)
        
...{
        
while ((vli [0& 1== 0)
            
...{
            p 
++;
            vli [
0>>= 1;
            }

        
return p;
        }


    
// 如果 vli 能被 POW2 整除则 vli /= POW2
    while (((vli [0+ vli [1* UnitBase) & mask) == 0)    // vli % POW2 == 0
        ...{
        p 
+= SHBITS;                // vli 能再多被 2 整除 SHBITS 次
        long long r = 0;        // r 为上一个单元除以 POW2 的余数
        for (int i = size - 1; i >= 0; i --)
            
...{
            
long long dx = r * UnitBase + vli [i];
            r 
= dx & mask;            // r = dx % POW2
            vli [i] = dx >> SHBITS;        // vli [i] = dx / POW2
            }

        
if (vli [size - 1== 0)        // 最高位为 0,则 size 减少 1
            ...{
            size 
--;
            }

        }

    
// 如果 vli 能被 2 整除则 vli /= 2
    while ((vli [0& 1== 0)
        
...{
        p 
++;
        
long long r = 0;
        
for (int i = size - 1; i >= 0; i --)
            
...{
            
long long dx = r * UnitBase + vli [i];
            r 
= dx & 1;
            vli [i] 
= dx >> 1;
            }

        
if (vli [size - 1== 0)
            
...{
            size 
--;
            }

        }

    
return p;
}



int test ()
...{
    scanf (
"%s"&str);
    len 
= strlen (str);
    str2vli ();
    
return times () + 1;
}


int main ()
...{
    
int t;
    scanf (
"%d"&t);
    
for (int i = 1; i < t; i ++)
        
...{
        printf (
"Case %d: %d ", i, test ());
        }

    printf (
"Case %d: %d ", t, test ());
    
return 0;
}


运行结果:

Run ID   User Name   Problem   Language   Status   Run Time   Run Memory   Submit Time
82506    rappizit  1028  C++  Accepted  0 sec  256 KB  2007-09-11 15:16:57

在那道题的排行榜上排第二^_^  

Rank   Submit Time   Run Time   Run Memory   Language   User

  1   2005-12-10 01:15:14   0.00S   148K   C   wangqiang

  2   2007-09-11 15:16:57   0.00S   256K   C++   rappizit

  3   2006-12-22 00:02:44   0.00S   260K   C++   jackeyyang

  4   2005-04-16 19:16:26   0.00S   264K   C++   Savior

  5   2006-08-16 00:52:04   0.00S   264K   C++   cockerel

==================================我是华丽的分割线==============================

大整数的各个单元是按数组的下标从低到高存放的,即 vli [0] 表示最低 9 位十进制数字。

判断一个整数能否被 2k 整除,等价于整数的最后 k 位十进制数字能被 2k 整除。

1 << k 即为 2k  ,a & ((1 << k ) - 1) 即为 a % 2k,a >> k 即为 a / 2k

采用 k = 18。而大整数是 109 进制的,因此判断大整数能否被 2k 整除,只要判断此条件为 true :

((vli [0] + vli [1] * UnitBase) & mask) == 0 ,其中 vli [0] + vli [1] * UnitBase 表示大整数的后 18 位十进制数字,UnitBase = 1,000,000,000 ,mask = (1 << 18 ) - 1 。


PS:这题是算法分析与设计课的第一次作业中的。之前没用109 进制来做,运行时间为 0.06S , 换成109 进制 且使用位运算就为 0.03S ,然后把输入输出换成 scanf  , printf 的就为 0.00S !(cin , cout 比较慢。)我稍微修改了一下代码然后用老师指定的 ID (csa+学号)再提交了一次,内存少用了 4K ,再次排在第二名,将原来的 ID rappizit 挤到第三名了,呵呵。。

 

珠海邀请赛个人解题报告 Problem I: Hanoi Tower Once More

Problem I: Hanoi Tower Once More Description     You know  the game of Hanoi Tower,  right? People s...
  • fudamou
  • fudamou
  • 2010年10月30日 13:52
  • 508

Hanoi Tower问题分析

前言 回家休息第3天了,状态一直不是太好,主要是要补牙,检查身体,见同学见亲戚,心里又着急校招,难得能腾出时间来好好思考,这里也是看,看到了汉诺塔问题,这里记录一下 思路分析 汉诺塔是递归的经典题...

Sicily 1028. Hanoi Tower Sequence

1028. Hanoi Tower Sequence Constraints Time Limit: 1 secs, Memory Limit: 32 MB Descriptio...

sicily 1028. Hanoi Tower Sequence

1028. Hanoi Tower Sequence Constraints Time Limit: 1 secs, Memory Limit: 32 MB Description Ha...

Sicily 1028 Hanoi Tower Sequence

汉诺塔问题,最经典的解法是递归,但是这道题的数据太大,没有办法实现。实际上这道题要求的是找出某一位置的汉诺块,没有必要完全模拟,而是要找出特定的汉诺塔会出现在哪些位置。 观察移动的汉诺塔块的序列,可...
  • wbchou
  • wbchou
  • 2013年02月17日 21:17
  • 482

<OJ_Sicily>Hanoi_Tower_Sequence

汉诺塔

joj 1031 Hanoi Tower Troubles Again!解题报告

  1031: Hanoi Tower Troubles Again!ResultTIME LimitMEMORY LimitRun TimesAC TimesJUDGE3s8192K1034606S...

汉诺塔问题 hanoi tower (递归)。

又来填坑了。 还是递归问题,这次是汉诺塔。 题意是这样的:有三根相邻的柱子,标号为A,B,C,A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘,要把所有盘子一个一个移动到柱子C上,并且每次移动同...

Hanoi Tower 汉诺塔的简单分析/C

当然、这是一个经典的递归问题~     想必来看这篇博文的同学对汉诺塔应该不会陌生了吧,   写这篇博还是有初衷的:   之前学数据结构的时候自己看书、也上网上查了很多资料,资料都比较散、而且描...

Tower of Hanoi

汉诺塔其实也就这么回事         初涉汉诺塔相关题目是在大一上学期C语言课递归章节,当时递归一知半解,汉诺塔一窍不通。经过了一年多的风霜洗礼,最近又见到汉诺塔的题,理解起来就容易多了。     ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Sicily 解题: 1028 Hanoi Tower Sequence
举报原因:
原因补充:

(最多只允许输入30个字)