新版汉诺塔(UVa10795 - A Different Task)

  • 题目介绍:

标准的汉诺塔上有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\n",t++,ans);
54 
55     }
56 
57     return 0;
58 
59 }
60 
61  

 

结语:

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

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

 

转载于:https://www.cnblogs.com/SeaSky0606/p/4569690.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实,而无需花费太多时间在底层功能的实上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请和数据,那么Go语言可能是一个不错的选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值