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;
}