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

来源:互联网 发布:磁卡刷卡器软件 编辑:程序博客网 时间:2024/05/29 08:07

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;}
0 0
原创粉丝点击