ZOJ Problem Set - 2338 The Towers of Hanoi Revisited
題目描述:
題目鏈接:Problem Set - 2338 The Towers of Hanoi Revisited
題目大意:
給定
解題思路:
對於整個移動的過程可以概括爲如下:
先把
k 個盤子,通過j跟柱子移動到中間的某根柱子上,步數:f[k][j] ;把剩下的
i−k 個盤子通過剩下的j−1 跟柱子移到第j跟柱上,步數:f[i−k][j−1] ;最後再把中間的
k 個盤子移到J柱上,步數:f[k][j] 。爲防止重複計算,且該題狀態有限
(64∗65) ,因此作爲預處理將所有的狀態所需最小步數求出。
再通過DFS 按照上述思路打印出移動步驟即可。
複雜度分析:
時間複雜度
空間複雜度
AC代碼:
#include <cstdio>
#include <stack>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn = 70;
const unsigned long long INF=0xffffffffffffffff;//定義無限大
unsigned long long f[maxn][maxn]; //f[i][j]表示i個盤,通過j個柱子,全程移動所需要的最少次數。
int pre[maxn][maxn]; //pre[i][j]表示f[i][j]的最優方案是先將最小的path[i][j]個盤移動到中間某根的柱子上
int n,m;//n個盤子 m個柱子
stack<int>stk[maxn];//每個柱子看做一個棧
bool used[maxn];//用來標記當前柱子是否爲空
void init(){ //初始化,預處理所有盤數與柱數的組合下,需要移動的次數
memset(f,INF,sizeof(f));
for(int i = 3; i <= 65; i++){
f[0][i] = 0;
f[1][i] = 1;
}//所有盤數爲0的情況,步數爲0.所有盤數爲1的情況,步數爲1
for(int i = 1; i <= 64; i++){
f[i][3] = f[i-1][3] * 2 + 1;
pre[i][3] = i - 1;
}//初始化三根柱子的情況
for(int i = 2; i <= 64; i++){//盤數從2起
for(int j = 4; j <= 65; j++){ //柱數從3起
for(int k = 1; k < i; k++){//爲達到最優方案處於中間柱上的盤數。
if(f[i][j] > f[i-k][j-1] + 2*f[k][j]){
f[i][j] = f[i-k][j-1] + 2*f[k][j];
pre[i][j] = k;
}
/*先把k個盤子,通過j跟柱子移動到中間的某根柱子上f[k][j];
把剩下的i-k個盤子通過剩下的j-1跟柱子移到第j跟柱子f[i-k][j-1];
最後再把中間的k個盤子移到J柱上f[k][j]。
共計f[i-k][j-1] + 2*f[k][j]*/
}
}
}
}
void dfs(int nt,int mt,int src,int des){//nt盤數,mt柱數,src起點,des終點
if(nt == 1){
if(stk[des].size())
printf("move %d from %d to %d atop %d\n",stk[src].top(),src,des,stk[des].top());
else
printf("move %d from %d to %d\n",stk[src].top(),src,des);
stk[des].push(stk[src].top());
stk[src].pop();
return;
}
int peg = 0; //柱子編號
for(int i = 1; i <= m; i++){
if(i != src && i != des && !used[i] ){
peg = i;
break;
}
}
dfs(pre[nt][mt],mt,src,peg);//先把k個盤子,通過j跟柱子移動到中間的某根柱子上
used[peg] = true;
dfs(nt - pre[nt][mt],mt-1,src,des);//把剩下的i-k個盤子通過剩下的j-1跟柱子移到第j跟柱子
used[peg] = false;
dfs(pre[nt][mt],mt,peg,des);//最後再把中間的k個盤子移到J柱上
}
int main(){
init();
int N;
cin >> N;
while(N--){
scanf("%d%d",& n,& m);
for(int i = 1; i <= m; i++)while(!stk[i].empty()) stk[i].pop();//清空棧
for(int i = n; i >= 1; i--)stk[1].push(i); //給第一根柱子上放盤
memset(used,false,sizeof(used));
printf("%llu\n",f[n][m]);
dfs(n,m,1,m);
}
return 0;
}