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用于反向打印移动过程。