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