枚举拆分点 k,先用四塔搬走 k 个最小盘,再用三塔搬剩下 n-k 个大盘,递推 f(n)=min(2f(k)+2^{n-k}-1)。
OJ: roj
题目 ID: 3009
难度:普及
标签:递归递推数学python
创建: 2026-10-01 09:57
更新: 2026-10-06 11:13
题目描述
有 4 根柱子 A、B、C、D 和
没有输入。
思路
四塔没有唯一中转柱,需要枚举"最大盘移动前已经搬走多少个最小盘"
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-01 09:57
# update_at: 2026-10-01 09:57
MAX_N = 12 # 题面要求输出 n = 1..12 的全部答案
def solve() -> None:
"""按四塔递推填出 f[1..MAX_N] 并逐行输出。"""
f = [0] * (MAX_N + 1) # f[n]:n 个盘从 A 柱搬到 D 柱的最少步数;f[0] = 0 表示空柱不用搬
for n in range(1, MAX_N + 1):
# 枚举第一个"拆点" k:先用四塔把 k 个最小盘搬到中转柱(f[k] 步),
# 再用三塔把剩下 n-k 个大盘搬到 D 柱(1 << (n - k) 再减 1 步,此时中转柱被小盘占住),
# 最后用四塔把 k 个最小盘接到 D 柱上(又是 f[k] 步)。
# k = 0 对应"不借第四座塔",即标准三塔搬法 2^n - 1 步,同样要参与取最小。
f[n] = min(2 * f[k] + (1 << (n - k)) - 1 for k in range(n))
print("\n".join(map(str, f[1:])))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-06 11:13
* update_at: 2026-10-06 11:14
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 12; // 题目要求输出 n = 1..12 的答案
ll f[MAXN + 1]; // f[n] 表示 n 个盘从 A 柱搬到 D 柱的最少步数,f[0] = 0
int main() {
// 四塔汉诺塔 Frame-Stewart 递推:
// 先用四塔把 k 个最小盘搬到中转柱(f[k] 步),
// 再用三塔把剩下 n-k 个大盘搬到 D 柱(2^{n-k} - 1 步),
// 最后用四塔把 k 个最小盘接到 D 柱(又是 f[k] 步)。
// k = 0 对应不借第四座塔,即标准三塔搬法。
for (int n = 1; n <= MAXN; n++) {
f[n] = (1LL << n) - 1; // 先取三塔上界
for (int k = 0; k < n; k++) {
ll cur = 2 * f[k] + (1LL << (n - k)) - 1;
if (cur < f[n]) f[n] = cur;
}
}
for (int n = 1; n <= MAXN; n++) {
printf("%lld\n", f[n]);
}
return 0;
}