# Dynamic Programming# Recursion# Math# Sequence

b161 - NOIP2007 4.Hanoi双塔问题

🔗 前往 ZeroJudge 原題

題目描述

題目要求計算將 2n 個圓盤從 A 柱移動到 C 柱,允許使用 B 柱暫存,且每次只能移動一個圓盤,並保持柱上圓盤大小順序的最小移動次數 An。

解題思路

本題可以透過觀察和數學歸納法來找到遞迴關係式。 當 n=1 時,有兩個圓盤,需要 2 步移動。 當 n=2 時,有四個圓盤,需要 6 步移動。 當 n=3 時,有六個圓盤,需要 14 步移動。 可以發現 An = 2 * An-1 + 2。 程式碼中,ans[i] 儲存了 n=i 時的最小移動次數。 程式碼使用動態規劃的方式預先計算出 ans[1]ans[200] 的值,然後根據輸入的 n 值直接輸出 ans[n]add 函數用於大數加法,因為移動次數可能會超出整數範圍。

複雜度分析

  • 時間複雜度: O(N) (預計算 ans 陣列) + O(1) (查詢) = O(N)
  • 空間複雜度: O(N) (儲存 ans 陣列)

程式碼

#include <iostream>
using namespace std;
int n;
string ans[251]={"0"};
inline string add(string a,string b){
	int tmp[10001]={0},al=a.length(),bl=b.length();
	string c;
	for(int i=0;i<al;++i)tmp[al-i-1]+=a[i]-'0';
	for(int i=0;i<bl;++i)tmp[bl-i-1]+=b[i]-'0';
	for(int i=0;i<10000;++i)
		if(tmp[i]>9){
			++tmp[i+1];
			tmp[i]-=10;
		}
	for(int i=10000;i>=0;--i)
		if(tmp[i]){
			al=i;
			break;
		}
	for(int i=al;i>=0;--i)c+=tmp[i]+'0';
	return c;
}
int main(){
	cin.tie(0); ios::sync_with_stdio(false);
	for(int i=0;i<250;++i)
		ans[i+1]=add(add(ans[i],ans[i]),"2");
	while(cin >> n)
		cout << ans[n] << "\n";
}

Discussion