zoj 2338

HanioTower的加强版


记f[n][m]为n个disc,m个peg的Hanoi问题,则有dp公式f[n][m]=min{f[n-k][m-1]+2*f[k][m]}。即把上面的k个disc利用m个peg转移某个中间peg,再把下面的n-k个disc利用m-1个peg转移到目标peg,最后把上面的k个disc利用m个peg移到目标peg。dp过程记下使得f[n][m]最小的g[n][m]=k用于反向打印移动过程。

 

#include<iostream>

#include<cstdio>

#include<cstring>

#include<algorithm>

#include <stack>

using namespace std;

typedef unsigned long long ll;

const ll INF=(~(0ULL))>>1;

ll dp[70][70];

int pre[70][70];

stack<int>v[80];

int n,m;

bool was[80];



void move(int from,int to) //从a这根柱子移到b这跟柱子

{

    if(v[to].empty()) printf("move %d from %d to %d\n",v[from].top(),from,to);

    else printf("move %d from %d to %d atop %d\n",v[from].top(),from,to,v[to].top());

    v[to].push(v[from].top());

    v[from].pop();

    return ;

}



void dfs(int ct,int from,int to,int h) //ct 表示盘子个数 a,b表示柱子标号 通过h根的柱子来进行操作

{

    int i,j,k;

    if(ct==1)

    {

        move(from,to);

        return ;

    }

    for(i=1;i<=m;i++)

        if(i!=from && i!=to && was[i]==0)break;

    dfs(pre[ct][h],from,i,h);

    was[i]=1;

    dfs(ct-pre[ct][h],from,to,h-1);

    was[i]=0;

    dfs(pre[ct][h],i,to,h);

}

void init(){

    int i,j,k;

    for(i=1;i<=70;i++) dp[i][3]=2*dp[i-1][3]+1,pre[i][3]=i-1;

    for(i=4;i<=65;i++){   //柱子

        dp[1][i]=1;

        for(j=2;j<=64;j++){  //盘子

            ll tem=INF;

            for(k=1;k<j;k++){ //先移走k个盘子到一个中间柱子,剩下j-k盘子移动到目标;

                if(tem > dp[j-k][i-1]+dp[k][i]*2){

                    tem=dp[j-k][i-1]+dp[k][i]*2;

                    pre[j][i]=k;

                }

            }

            dp[j][i]=tem;

        }

    }

}

int main()

{

    int i,j,k,ca,kk;



    init();

    scanf("%d",&ca);

    while(ca--)

    {

        scanf("%d%d",&n,&m);

        printf("%lld\n",dp[n][m]);

        for(i=1;i<=m;i++)while(!v[i].empty())v[i].pop();

        for(i=n;i>=1;i--) v[1].push(i);

        memset(was,0,sizeof(was));

        dfs(n,1,m,m);

    }

    return 0;

}


 

记f[n][m]为n个disc,m个peg的Hanoi问题,则有dp公式f[n][m]=min{f[n-k][m-1]+2*f[k][m]}。即把上面的k个disc利用m个peg转移某个中间peg,再把下面的n-k个disc利用m-1个peg转移到目标peg,最后把上面的k个disc利用m个peg移到目标peg。dp过程记下使得f[n][m]最小的g[n][m]=k用于反向打印移动过程。

 

你可能感兴趣的:(ZOJ)