• 新版汉诺塔(UVa10795


    • 题目介绍:

    标准的汉诺塔上有n个大小各异的盘子。现给定一个初始局面(见图1),求它到目标局面(见图2)至少需要移动多少步?

    移动规则:一次只能移动一个盘子;且在移动盘子之前,必须把压在上面的其他盘子先移走;基于汉诺塔问题的原始约定,编号大的盘子不得压在编号小的盘子上。

     

     

     

    Sample Input 

    3

    1 1 1

    2 2 2

    3

    1 2 3

    3 2 1

    4

    1 1 1 1

    1 1 1 1

    0

     

    Sample Output 

    Case 1: 7

    Case 2: 3

    Case 3: 0

    • 问题分析:

    为了更好的剖析问题。我们首先考虑编号最大的盘子。显然,如果这个盘子的在初始局面和目标局面中位于同一根柱子,那么我们可以根本不需要移动它。直接忽略它在两个局面的存在。

    设现在存在初始局面跟目标局面中位置不同的盘子最大编号为k。现在设想一下移动k之前的瞬间。不妨假设盘子k需要从柱子A移动到柱子B,那么在移动k之前的局面必然是,1,2...k-1全部位于柱子C,而且从上到下排好序。我们把这个局面称为参考局面。

    根据对称性,我们只需要求出初始局面和目标局面到参考局面移动的步数之和,再加上1(移动编号为k的盘子)即可。

    现定义这样的一个函数 f(arr,k,flag):表示已知各盘子的初始编号为数组arr,把1,2...k移动到flag柱子所需要的最少步数。可得本题答案表示如下:

    ans = f(start,k-1,6-start[k]-finish[kl) + f(finish,k-1,6-start[k]-finish[kl) + 1

    将问题分解之后,我们再考虑如何基于汉诺塔的性质,递归求解f(arr,k,flag)

    显然,k=0时意味着没有盘子需要移动,此时返回0,作为递归跳出的判断条件;

    K!=0时,比较arr[k]==flag? 如果相等,那么很好办,直接f(arr,k,flag) = f(arr,k-1,flag)即可,因为编号k不需要移动。当arr[k]!=flag时就需要推导一下了。我们把“1,2...k-1”看做一个整体,此时移动k前后需要将整体从一个柱子移动到另一个柱子,而根据汉诺塔的经典理论,将n个盘子初始有序的盘子由一个柱子移动到另一个柱子最少需要:2^n - 1 次。本题中,我们还要加上移动盘子k的一次操作,故最后:

    f(arr,k,flag) = f(arr,k-1,6-arr[k]-flag)  +  (1<<(k-1))

     

     

    • 参考代码:

     1 #include <cstdio>
     2 
     3 typedef long long ll;
     4 
     5 const int maxn=64;
     6 
     7 int a[maxn],b[maxn];
     8 
     9  
    10 
    11 ll f(int *a,int k,int flag){
    12 
    13     if(k<1) return 0;
    14 
    15     else if(a[k]==flag){
    16 
    17         return f(a,k-1,flag);
    18 
    19     }else{
    20 
    21         return f(a,k-1,6-a[k]-flag) + (1LL<<(k-1));    //"1LL"自动转换为long long 类型
    22 
    23     }
    24 
    25 }
    26 
    27 int main(){
    28 
    29     int n,t=1;
    30 
    31     while(scanf("%d",&n)==1 && n){
    32 
    33         for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    34 
    35         for(int i=1;i<=n;i++) scanf("%d",&b[i]);
    36 
    37  
    38 
    39         //find 'k'
    40 
    41         int k=n;
    42 
    43         while(a[k]==b[k] && k>=1)k--;
    44 
    45  
    46 
    47         ll ans=0;
    48 
    49         ans = f(a,k-1,6-a[k]-b[k]) + f(b,k-1,6-a[k]-b[k]) + 1;
    50 
    51         if(k==0) ans = 0;
    52 
    53         printf("Case %d: %lld
    ",t++,ans);
    54 
    55     }
    56 
    57     return 0;
    58 
    59 }
    60 
    61  

    结语:

    这道题从刚开始入手的杂乱通过一步步转换推导之后,最终程序的精简实现不由得让人拍案叫绝!本文解析或许词不达意,不到之处请谅解。同时,欢迎有其他思路或想法的朋友私下交流讨论。

    (hint:提交本题目时注意数据类型选用64位整型数long long”(1<<(k-1)”若没有加上“LL”则提交结果为WA! )

     

  • 相关阅读:
    2021暑假模拟赛6
    2021暑假模拟赛5
    2021暑假模拟赛4
    2021暑假模拟赛3
    2021暑假模拟赛2
    umi提速方案之 mfsu
    Nginx (可为容器)配置 BasicAuth 与访问
    Mango 漫画管理器体验
    blivechat 在 OBS 中使用 BasicAuth URL 登录
    Electron 实现最小化到托盘
  • 原文地址:https://www.cnblogs.com/SeaSky0606/p/4569690.html
Copyright © 2020-2023  润新知