ZOJ Problem Set - 2338 The Towers of Hanoi Revisited DFS+预处理

ZOJ Problem Set - 2338 The Towers of Hanoi Revisited

题目描述:

  题目链接:Problem Set - 2338 The Towers of Hanoi Revisited

题目大意:

  给定N1<=N<=64 个盘子和M4<=M<=65 根柱子,问把N 个盘子从1 号柱子移动到M 号柱子所需要的最少步数,并且输出移动过程。

解题思路:

  对于整个移动的过程可以概括为如下:

  • 先把k 个盘子,通过j跟柱子移动到中间的某根柱子上,步数:f[k][j] ;

  • 把剩下的ik 个盘子通过剩下的j1 跟柱子移到第j跟柱上,步数:f[ik][j1]

  • 最后再把中间的k 个盘子移到J柱上,步数:f[k][j]

      为防止重复计算,且该题状态有限6465 ,因此作为预处理将所有的状态所需最小步数求出。
    再通过DFS 按照上述思路打印出移动步骤即可。

复杂度分析:

时间复杂度 O(n2)
空间复杂度 O(n2)

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;
}
发布了31 篇原创文章 · 获赞 0 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章