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用于反向打印移动过程。
原文地址:https://www.cnblogs.com/javawebsoa/p/3001382.html