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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章