BZOJ 1019 - [SHOI2008]汉诺塔
Published on 2016-08-25描述
汉诺塔由三根柱子(分别用 , , 表示)和 (本题 ) 个大小互不相同的空心盘子组成。一开始 个盘子都摞在柱子 上,大的在下面,小的在上面,形成了一个塔状的锥形体。
对汉诺塔的一次合法的操作是指:从一根柱子的最上层拿一个盘子放到另一根柱子的最上层,同时要保证被移动的盘子一定放在比它更大的盘子上面(如果移动到空柱子上就不需要满足这个要求)。我们可以用两个字母来描述一次操作:第一个字母代表起始柱子,第二个字母代表目标柱子。例如,AB
就是把柱子 最上面的那个盘子移到柱子 。汉诺塔的游戏目标是将所有的盘子从柱子 移动到柱子 或柱子 上面。有一种非常简洁而经典的策略可以帮助我们完成这个游戏。首先,在任何操作执行之前,我们以任意的次序为六种操作(AB
、AC
、BA
、BC
、CA
和 CB
)赋予不同的优先级,然后,我们总是选择符合以下两个条件的操作来移动盘子,直到所有的盘子都从柱子 移动到另一根柱子:
- 这种操作是所有合法操作中优先级最高的;
- 这种操作所要移动的盘子不是上一次操作所移动的那个盘子。
可以证明,上述策略一定能完成汉诺塔游戏。现在你的任务就是假设给定了每种操作的优先级,计算按照上述策略操作汉诺塔移动所需要的步骤数。
保证答案不会超过 。
分析
模拟肯定是不行的, 跑 5 个小时都跑不完。
在原版的汉诺塔问题中,步骤具有明显的递归性质,虽然本题规则有所改变,我们必须朝着递归的方向思考。
首先可以肯定的是,本题的步骤是唯一的,所以我们不需要通过动态规划来进行最优决策,任何情况下都只有一种选择。
在解题之前先提醒一下本题的一个重要信息:“这种操作所要移动的盘子不是上一次操作所移动的那个盘子”。
设 为 中有 个盘子,且除 以外其余柱子中没有盘子,将 中的全部盘子移到某一个盘子的最小步数,我们 柱的盘子移到了 中。
显然 为答案( 为 0, 为 1, 为 2)。考虑边界 ,根据优先级,我们可以算出 ,优先级仅在这一步有用。
考虑如何计算 ,注意,只有 柱有 个盘子!为了转移 上的 个盘子,我们必须将 个盘子先全部移动到某个柱子上(也必定有一个时刻是这样的,否则不可能结束游戏),设移动到的柱子为 ,则另外一个柱子为 。为什么可以使用 呢?因为 最底下最大的盘子是无用的,如果要移动它,说明有一个柱子是空的,说明已经有一个柱子上有 个盘子了,与最小步数违背!所以可以直接忽略最大的盘子。
我们知道,现在 上有一个盘子, 上有 个盘子,而且之前一步进行的操作一定是把某个盘子移到 上。现在只能动 上面最大的盘子了,我们把它移到 ,现在又只能动 了。
考虑 的取值:
- 如果 ,那么直接将 上的 个移动到 ,任务完成,,总共的花费为 。
- 如果 ,那么先将 上的 个移动到 ,再将 上最大的盘子移动到 ,再将 上的 个盘子移动到 ,任务完成,,总共的花费为 。
实现起来非常简单,复杂度: 。
代码
// Created by Sengxian on 08/23/16. // Copyright (c) 2016年 Sengxian. All rights reserved. // BZOJ 1019 递推 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 30 + 3; int n, g[maxn][3]; ll f[maxn][3]; bool vis[3]; int main() { #ifndef ONLINE_JUDGE freopen("test.in", "r", stdin); #endif scanf("%d", &n); for (int i = 0; i < 6; ++i) { static char str[10]; scanf("%s", str); int a = str[0] - 'A', b = str[1] - 'A'; if (vis[a]) continue; vis[a] = true, f[1][a] = 1, g[1][a] = b; } for (int j = 2; j <= n; ++j) for (int i = 0, a, b; i < 3; ++i) { a = g[j - 1][i], b = 3 - i - a; if (g[j - 1][a] == b) f[j][i] = f[j - 1][i] + 1 + f[j - 1][a], g[j][i] = b; else f[j][i] = f[j - 1][i] + 1 + f[j - 1][a] + 1 + f[j - 1][i], g[j][i] = a; } printf("%lld\n", f[n][0]); return 0; }