目录
题目
河内塔问题:有三个柱子,编号为1,2,3;在编号为1的柱子上有n个大小不同圆盘,圆盘从小到大,从上到下堆叠,你只可以移动一个柱子上最上面的圆盘。
现在你需要将编号为1的柱子上的圆盘移到3柱子上,顺序不变;
注意:你在移动过程中,不可以将大的圆盘放在小圆盘上,你一次只可以移动一个盘子;
现在有一个4个柱子的河内塔,在规则不变的情况下,问最少需要移动多少次才能把盘子从1号柱子移到4号柱子上。
格式
输入格式:一个整数f,表示n取(1,f)的f种情况。
输出格式:输出f行,表示当n分别取(1,f)的情况下,需要的最少移动次数。
样例
输入:
12输出:
1 3 5 9 13 17 25 33 41 49 65 81
思路
本题为经典的汉诺塔问题,使用动态规划dp求解。
(1)当柱子总数为3(假设有A、B、C三个柱子):
将n个盘子分为n-1个和底下那一个,那么将这n个盘子移动到指定柱子上就分为两个步骤:移动n-1个盘子所需要的移动次数和底下一个盘子的移动次数。
用dp[n]表示移动n个盘子所需要的移动次数,那么有公式:
推导原理:当n个盘子在A柱上时,首先将上面n-1个盘子移动到B柱,这个移动次数为dp[n-1]。然后将底下的一个盘子直接移动到C柱,这个移动次数为1。最后需要将B柱上的n-1个盘子移动到C柱上,移动次数为dp[n-1]。
三柱的公式可以进一步归纳为:
(2)当柱子总数为4时(假设有A、B、C、D四个柱子):
将n个盘子分为n-k个和底下k个,那么将这n个盘子移动到指定柱子上就分为两个步骤:移动n-k个盘子所需要的移动次数和底下k个盘子的移动次数。将前n-k个盘子暂时移动到B柱上之后,问题便转化为了:三柱时,将k个盘子移动到指定柱子上。因为底下的盘子比上面n-k个盘子大,故移动k个盘子时不会再用到B柱。
用dp[n]表示移动n个盘子所需要的移动次数,那么有公式:
化简一下即为:
代码
明确思路后,代码就很简短啦
#include <bits/stdc++.h>
using namespace std;
int f;
int dp[55], INF = 0x3f3f3f3f;
int main()
{
cin >> f;
//当盘子数量为1和2时,直接输出
cout << 1 << endl << 3 << endl;
memset(dp, INF, sizeof(dp));
dp[0] = 0;
dp[1] = 1;
dp[2] = 3;
//遍历盘子数,从3到f
for (int x = 3; x <= f; x++)
{
for (int k = 1; k < x; k++) //将x个盘子分为两层:上面x-k个,下面k个
{
//记住这里虽然没有其他更新条件,但是动态规划的更新基础是比原来方案更好才更新
if (2 * dp[x - k] + pow(2, k) - 1 < dp[x]) //这里是移动次数,当然是越小越好!!
{
dp[x] = 2 * dp[x - k] + pow(2, k) - 1;
}
}
cout << dp[x] << endl;
}
return 0;
}
注:本题解题思路来源b站up主:轩哥码题,有需要可跳转B站看up详细讲解。附up链接:b站up个人主页