一、经典汉诺塔
题意
有三根柱子 A,B,C,柱子 A 上放着 n 个大小不同的盘子,从上往下,盘子大小依次增大。要把所有盘子都移到柱子B上,且移动的过程中,同一根柱子不能出现大盘子在小盘子上方的情况,问至少需要多少次移动。
分析
设 F[n] 为将 n 个盘子从柱子A移动到柱子B的最少移动次数。
当 n = 1 时,F[n] = 1
当 n > 1 时,分三步
①将柱子A上的 n - 1 个盘子,依靠柱子B,移动到柱子C上,这个过程至少需要 F[n - 1]步
②将柱子A上最大的那个盘子放到柱子B上,需要 1 步
③将柱子C上的 n - 1 个盘子,依靠柱子A,移动到柱子B上,这个过程至少需要 F[n - 1]步
所以移动完 n 个盘子,至少需要的步数 F[n] = 2 * F[n - 1] + 1;
其中 F[1] = 1,那么就有 F[n] = 2^n - 1;
二、HDU 1207 汉诺塔Ⅱ (传送门)
题意
有四根柱子 A,B,C,D,柱子 A 上放着 n 个大小不同的盘子,从上往下,盘子大小依次增大。要把所有盘子都移到柱子B上,且移动的过程中,同一根柱子不能出现大盘子在小盘子上方的情况,问至少需要多少次移动。(1 <= n <= 64)
分析
设 F[n] 为将 n 个盘子从柱子A移动到柱子B的最少移动次数。
当 n = 1 时,F[n] = 1;
当 n = 2 时,F[n] = 3;
当 n > 2 时,分三步
①将柱子A上的 x (1 <= x < n) 个盘子,依靠柱子B,C移动到柱子D上,这个过程至少需要 F[x] 步
②将柱子A上剩下的 n - x 个盘子,依靠柱子C移动到柱子B上,这个过程相当于经典汉诺塔问题,则需要 2^(n-x) - 1 步
③将柱子D上的 x 个盘子,依靠柱子A,C移动到柱子B上,这个过程至少需要 F[x] 步。
那我们可以枚举这个 x,对所有可能的答案取最小值
当 n = 64 时,x = 0 时,2^64 就很大了,会爆 long long,所以我特判掉了这种情况
#include <bits/stdc++.h> #define LL long long #define ULL unsigned long long #define UI unsigned int #define mem(i, j) memset(i, j, sizeof(i)) #define rep(i, j, k) for(int i = j; i <= k; i++) #define dep(i, j, k) for(int i = k; i >= j; i--) #define pb push_back #define make make_pair #define INF 0x3f3f3f3f #define inf LLONG_MAX #define PI acos(-1) #define fir first #define sec second #define lb(x) ((x)h & (-(x))) #define dbg(x) cout<<#x<<" = "<<x<<endl; using namespace std; const int N = 1e6 + 5; const LL mod = 1e9 + 7; LL ksm(LL a, LL b) { LL res = 1LL; while(b) { if(b & 1) res = res * a; a = a * a; b >>= 1; } return res; } LL f[N]; void solve() { f[1] = 1; f[2] = 3; rep(i, 3, 64) { f[i] = inf; rep(j, 0, i - 1) { if(i - j >= 63) continue; f[i] = min(f[i], 2 * f[j] + ksm(2, i - j) - 1); } } int n; while(~scanf("%d", &n)) printf("%lld\n", f[n]); } int main() { // int _; scanf("%d", &_); // while(_--) solve(); solve(); return 0; }