算法竞赛进阶指南打卡系列题解
题目
原题链接
题面
汉诺塔问题,条件如下:
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
1≤n≤12
输入样例:
没有输入
输出样例:
参考输出格式
题解
思路
这竟然是个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[n−1]+1 ,即:
先把
n
−
1
n-1
n−1 个盘从
A
A
A 柱移动到
B
B
B 柱需要
d
[
n
−
1
]
d[n-1]
d[n−1] 步,
再把最后一个盘从
A
A
A 柱移动到
C
C
C 柱需要
1
1
1 步,
最后把
B
B
B 柱上的
n
−
1
n-1
n−1 个盘从
B
B
B 柱移动到
C
C
C 柱需要
d
[
n
−
1
]
d[n-1]
d[n−1] 步。
共需
d
[
n
]
=
2
×
d
[
n
−
1
]
+
1
d[n]=2×d[n-1]+1
d[n]=2×d[n−1]+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[n−i]) 。
初始化:
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
n−i 个盘子在三塔模式下移动到
D
D
D 柱(由于不能到
B
B
B 上,因此等同于三柱)需要
d
[
n
−
i
]
d[n-i]
d[n−i] 步。
最后把剩下的
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[n−i]) 步。
综上所述,我们预处理出 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;
}