• 题面描述

    • 汉诺塔由三根柱子(分别用\(A\ B\ C\)表示)和\(n\)个大小互不相同的空心盘子组成。一开始\(n\)个盘子都摞在柱子\(A\)上,大的在下面,小的在上面,形成了一个塔状的锥形体。

      对汉诺塔的一次合法的操作是指:从一根柱子的最上层拿一个盘子放到另一根柱子的最上层,同时要保证被移动的盘子一定放在比它更大的盘子上面(如果移动到空柱子上就不需要满足这个要求)。我们可以用两个字母来描述一次操作:第一个字母代表起始柱子,第二个字母代表目标柱子。例如,\(AB\)就是把柱子\(A\)最上面的那个盘子移到柱子\(B\)。汉诺塔的游戏目标是将所有的盘子从柱子\(A\)移动到柱子\(B\)或柱子\(C\)上面。有一种非常简洁而经典的策略可以帮助我们完成这个游戏。首先,在任何操作执行之前,我们以任意的次序为六种操作\((AB,AC,BA,BC,CA,CB)\)赋予不同的优先级,然后,我们总是选择符合以下两个条件的操作来移动盘子,直到所有的盘子都从柱子\(A\)移动到另一根柱子:

      • 这种操作是所有合法操作中优先级最高的;

      • 这种操作所要移动的盘子不是上一次操作所移动的那个盘子。

      可以证明,上述策略一定能完成汉诺塔游戏。现在你的任务就是假设给定了每种操作的优先级,计算按照上述策略操作汉诺塔移动所需要的步骤数。

  • 输入格式

    • 输入有两行。第一行为一个整数\(n\ (1\leq n\leq 30)\),代表盘子的个数。第二行是一串大写的\(ABC\)字符,代表六种操作的优先级,靠前的操作具有较高的优先级。每种操作都由一个空格隔开。
  • 输出格式

    • 只需输出一个数,这个数表示移动的次数,保证答案\(\leq 10^{18}\)
  • 题解

    • 首先当所有可能的操作都被赋予优先级,并且限制相邻两次操作不能移同一个对象,因此该汉诺塔的移动序列已经被固定。

    • 我们要做的就从最优化变成了合并操作序列中相同的部分,加速计算效率

    • 类比普通的汉诺塔问题,不难发现在移盘子的时候总会不时出现将\(1,.., x\)个盘子以同一个操作序列从某个地方移到某个地方。

    • 令三根柱子分别叫\(x,y,z\)

    • 是这个启发,我们可以设计出如下状态:

      • \(f[i][x][y]\)表示\(1,..,i\)个盘子从\(x\)移到\(y\)所需的步数
      • 特别的,当\(f[i][x][y]==-1\)时表示\(1,..,i\)个盘子从\(x\)移到\(y\)不合法
    • 这样我们可以写出转移方程

      • \[f_{i,x,y}=f_{i-1,x,y}+1+f_{i-1,y,x}+1+f_{i-1,x,y}\\
        或\\
        f_{i,x,y}=f_{i-1,x,z}+1+f_{i-1,z,y}
        \]

      • 注意到当\(1,..,i-1\)个盘子没有移完的时候,我们是不可能去移第\(i\)个盘子的

        • 分两种情况

          • 一,还有一些盘子在第\(i\)个盘子上,那我们不可能移动第\(i\)个盘子
          • 二,第\(i\)个盘子上已经没有盘子了,又分两种情况
            • \(1,..,i-1\)个盘子在同一根柱子,那么这时候我们称这\(i-1\)个盘子已经移好了,与假设不符
            • \(1,..,i-1\)个盘子不在同一根柱子,那么我们不能移第\(i\)个盘子,因为如果能够移动第\(i\)个盘子,移动到的柱子上已没有比\(i\)小的盘子,但此时两根柱子都有\(1,..,i-1\)中的某一些盘子
      • 也就是说,我们必须把\(1,..,i-1\)个盘子移好再移第\(i\)个盘子

      • 故第一个递推方程表示

        • 当\(1,..,i-1\)个盘子从\(x\)移到\(y\)合法,且\(1,..,i-1\)个盘子从\(y\)移到\(x\)合法时,

          • 我们先将\(1,..,i-1\)个盘子从\(x\)移到\(y\),花费\(f_{i-1,x,y}\)步。
          • 因为相邻两次操作对象不能相同,我们只能将第\(i\)个盘子从\(x\)移到\(z\),花费\(1​\)步。
          • 因为相邻两次操作对象不能相同,我们只能移\(1,..,i-1\)盘子,同时我们不能把这\(1,..,i-1\)盘子移到第\(i\)个盘子所在的\(z\)柱子,因为如此一来,我们在下一步又只能移\(1,..,i-1\)盘子,这是不合法的。因此我们只能把\(1,..,i-1\)盘子移到\(x\)柱子,花费\(f_{i-1,y,x}\)步
          • 因为相邻两次操作对象不能相同,我们只能将第\(i\)个盘子从\(x\)移到\(y\),花费\(1\)步。
          • 最后我们将\(1,..,i-1\)移回\(y\),花费\(f_{i-1,x,y}\)步
      • 第二个递推方程表示

        • 当\(1,..,i-1\)盘子从\(x\)移到\(z\)合法,且\(1,..,i-1\)盘子从\(z\)移到\(y\);合法时,

          • 我们将\(1,..,i-1\)盘子从\(x\)移到\(z\),花费\(f_{i-1,x,z}\)步
          • 因为相邻两次操作对象不能相同,我们只能将第\(i\)个盘子从\(x\)移到\(y\),花费\(1\)步。
          • 最后我们将\(1,..,i-1\)盘子从\(z\)移回\(y\),花费\(f_{i-1,z,y}\)步
    • 同时因为操作的优先级,我们不难发现这两个递推是不可能同时成立,但有可能同时不成立。因此当这两个递推式同时不成立时\(f_{i,x,y}=-1\)

    • 关于边界条件,根据题目给出的操作优先级确定

      • 例如:从\(x\)向其他柱子移动的操作优先级为\(xy>xz\),因此\(f_{1,x,y}=1,f_{1,x,z}=-1\)
    • 如此递推即可

    • 网上好多博客一本正经地说了一个奇怪的\(dp\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=55;
typedef long long ll;
ll f[MAXN][4][4];
int n;
char s[10][4];
int main(){
scanf("%d",&n);
for (int i=1;i<=6;i++) scanf("%s",s[i]);
for (int i=0;i<3;i++){
f[1][i][0]=f[1][i][1]=f[1][i][2]=-1;
for (int j=1;j<=6;j++){
if (s[j][0]-'A'==i){
int to=s[j][1]-'A';
f[1][i][to]=1;
break;
}
}
}
for (int i=2;i<=n;i++){
for (int j=0;j<3;j++){
for (int k=0;k<3;k++){
int l=3-j-k;
f[i][j][k]=-1;
if (f[i-1][j][k]!=-1&&f[i-1][k][j]!=-1){
f[i][j][k]=f[i-1][j][k]*2+2+f[i-1][k][j];
}
if (f[i-1][j][l]!=-1&&f[i-1][l][k]!=-1){
f[i][j][k]=f[i-1][j][l]+1+f[i-1][l][k];
}
// cout<<i<<" "<<j<<" "<<k<<" "<<f[i][j][k]<<endl;
}
}
}
if (f[n][0][1]!=-1) printf("%lld\n",f[n][0][1]);
else printf("%lld\n",f[n][0][2]);
return 0;
}

随机推荐

  1. myisam innodb memory 区别(2)

    1.区别:1) MyISAM管理非事务表.提供高速存储和检索,以及全文搜索能力.MyISAM在所有MySQL配置里被支持,是默认的存储引擎,除非配置MySQL默认使用另外一个引擎.2)MEMORY存储 ...

  2. 3.3.7 跳表 SkipList

    一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下,ConcurrentHashMap 存取速度是ConcurrentSki ...

  3. js总结33 :javascript-DOM节点属性

    1 设置节点属性三个方法: 获取:getAttribute(名称) 设置:setAttribute(名称, 值) 删除:removeAttribute(名称) 举个例子: <!DOCTYPE h ...

  4. kalilinux-信息搜集

    dns扫描: dnsenum --enum www.baidu.com --threads [number] 一次运行的线程数量 -r 递归查找 -d 允许你设置在WHOIS请求之间的时间延迟,单位为 ...

  5. WCF服务编程 读书笔记——第1章 WCF基础(2)

    续:第1章 WCF基础(1) 元数据交换 服务有两种方案可以发布自己的元数据.一种是基于HTTP-GET协议提供元数据, 另一种则是后面将要讨论的使用专门的终结点的方式.WCF能够为服务自动提供基于H ...

  6. 编写高质量代码改善C#程序的157个建议——建议147:重构多个相关属性为一个类

    建议147:重构多个相关属性为一个类 若存在多个相关属性,就应该考虑是否将其重构为一个类.查看如下类: class Person { public string Address { get; set; ...

  7. 编写高质量代码改善C#程序的157个建议——建议81:使用Parallel简化同步状态下Task的使用

    建议81:使用Parallel简化同步状态下Task的使用 在命名空间System.Threading.Tasks中,有一个静态类Parallel简化了在同步状态下的Task的操作.Parallel主 ...

  8. [Selenium With C#基础教程] Lesson-04 按钮

    作者:Surpassme 来源:http://www.jianshu.com/p/83d7416c4b7d 声明:本文为原创文章,如需转载请在文章页面明显位置给出原文链接,谢谢. Button通常有两 ...

  9. CSS 形状绘制

                                      最后一个 先放代码 <style type="text/css"> #heart { positio ...

  10. 设计模式6---代理模式(Proxy Pattern)

    代理设计模式 定义:为其他对象提供一种代理以控制对这个对象的访问. 1.  静态代理 静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象都实现相同的接口或者是继承相同父类. 接口:IUser ...