算法竞赛进阶指南打卡系列 0x00 9.奇怪的汉诺塔

算法竞赛进阶指南打卡系列题解

0x00 9.奇怪的汉诺塔


题目

原题链接

AcWing 96. 奇怪的汉诺塔

题面

汉诺塔问题,条件如下:

1、这里有 A A A B B B C C C D D D 四座塔。

2、这里有 n n n 个圆盘, n n n 的数量是恒定的。

3、每个圆盘的尺寸都不相同。

4、所有的圆盘在开始时都堆叠在塔 A A A 上,且圆盘尺寸从塔顶到塔底逐渐增大。

5、我们需要将所有的圆盘都从塔 A A A 转移到塔 D D D 上。

6、每次可以移动一个圆盘,当塔为空塔或者塔顶圆盘尺寸大于被移动圆盘时,可将圆盘移至这座塔上。

请你求出将所有圆盘从塔 A A A 移动到塔 D D D,所需的最小移动次数是多少。

汉诺塔塔参考模型汉诺塔塔参考模型

输入格式
没有输入

输出格式
对于每一个整数 n n n,输出一个满足条件的最小移动次数,每个结果占一行。

数据范围
1 ≤ n ≤ 12 1≤n≤12 1n12

输入样例:
没有输入

输出样例:
参考输出格式

题解

思路

这竟然是个DP题。

首先考虑三柱经典汉诺塔问题,我们不妨使用 d [ n ] d[n] d[n] 表示求解 n n n 盘三柱汉诺塔问题所需要的最小步数,那么 d [ n ] = 2 × d [ n − 1 ] + 1 d[n]=2×d[n-1]+1 d[n]=2×d[n1]+1 ,即:
先把 n − 1 n-1 n1 个盘从 A A A 柱移动到 B B B 柱需要 d [ n − 1 ] d[n-1] d[n1] 步,
再把最后一个盘从 A A A 柱移动到 C C C 柱需要 1 1 1 步,
最后把 B B B 柱上的 n − 1 n-1 n1 个盘从 B B B 柱移动到 C C C 柱需要 d [ n − 1 ] d[n-1] d[n1] 步。
共需 d [ n ] = 2 × d [ n − 1 ] + 1 d[n]=2×d[n-1]+1 d[n]=2×d[n1]+1 步。

由此扩展到四柱汉诺塔问题为 f [ n ] = m i n ( 2 × f [ i ] + d [ n − i ] ) f[n]=min(2×f[i]+d[n-i]) f[n]=min(2×f[i]+d[ni])
初始化: f [ 1 ] = 1 f[1]=1 f[1]=1 表示一个盘子在四塔模式下移动到 D D D 需要 1 1 1 步。
我们可以先把i个盘子在四塔模式下移动到 B B B 柱需要 f [ i ] f[i] f[i] 步。
然后把 n − i n-i ni 个盘子在三塔模式下移动到 D D D 柱(由于不能到 B B B 上,因此等同于三柱)需要 d [ n − i ] d[n-i] d[ni] 步。
最后把剩下的 i i i 个盘子在四塔模式下移动到 D D D 柱上需要 f [ i ] f[i] f[i] 步。
共需 f [ n ] = m i n ( 2 × f [ i ] + d [ n − i ] ) f[n]=min(2×f[i]+d[n-i]) f[n]=min(2×f[i]+d[ni]) 步。

综上所述,我们预处理出 d d d 然后枚举 i i i f f f 即可。

神奇的思路,没做过感觉根本想不到。

代码

#include <bits/stdc++.h>

// #define int long long

using namespace std;

constexpr int P = 998244353;
using i64 = long long;

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    vector<int> d(13);
    for (int n = 1; n <= 12; n ++ ) d[n] = 2 * d[n - 1] + 1;
	
	// 由于 f 要取 min ,因此初始化为无穷大 1e9
    vector<int> f(13, 1e9);
    // 因为全部初始化为 1e9 ,因此此处初始化很重要
    f[1] = 1; 
    for (int n = 2; n <= 12; n ++ ) {
        for (int i = 0; i <= n; i ++ ) {
            f[n] = min(f[n], 2 * f[i] + d[n - i]);
        }
    }

    for (int n = 1; n <= 12; n ++ ) cout << f[n] << " ";

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值