还是太naive了,在知道经典汉诺塔的情况下,居然很久没想出来。其实多根棍子和三根是一个原理。n个盘子,m根棍子的最优解,一定是把一部分盘子(假设为x)先移到某个中间棍子上,也就是先解决子问题(x,m),然后把剩下的n-x个盘子通过剩下的m-1根棍子用最优方案转移,即子问题(n-x,m-1),最后把中间棍子上的那x个盘子移过去,即子问题(x,m)。这样可以用dp来求解。
#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <string>
#include <memory.h>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
#define ll unsigned long long
ll dp[77][77];
int num[77][77];
ll fun(int n,int m){
if(dp[n][m]){
return dp[n][m];
}
if(n==0){
return 0;
}else if(n==1){
return 1;
}else if(m==2){
return n;
}else if(m==3){
num[n][m] = n-1;
dp[n][m] = fun(n-1,3)*2+1;
return dp[n][m];
}
ll res = (~(0ULL))>>1;;
for(int i=1;i<n;i++){
ll tmp = fun(i,m)*2+fun(n-i,m-1);
if(tmp<=res){
res = tmp;
num[n][m] = i;
}
}
dp[n][m] = res;
return res;
}
stack<int> pegs[77];
void move(int from,int to){
if(pegs[to].size()){
printf("move %d from %d to %d atop %d\n",pegs[from].top(),from,to,pegs[to].top());
pegs[to].push( pegs[from].top() );
pegs[from].pop();
}else{
printf("move %d from %d to %d\n",pegs[from].top(),from,to);
pegs[to].push( pegs[from].top() );
pegs[from].pop();
}
}
int M;
void solve(int n,int m,int from,int to){
if(n==1){
move(from,to);
return;
}
if(m==2){
while(n--){
move(from,to);
}
return;
}
stack<int> where;
stack<int> how;
int curn = n;
int curm = m;
for(int i=1;i<=M;i++){
if(i==from || i==to)continue;
if(pegs[i].size() && pegs[from].top()>pegs[i].top())continue;
int tmp = num[curn][curm];
if(tmp==0){
break;
}
if(curn==1)break;
solve(tmp,curm,from,i);
where.push(i);
how.push(tmp);
curn-=tmp;
curm--;
}
curm++;
move(from,to);
while(where.size()){
solve(how.top(),curm,where.top(),to);
how.pop();
where.pop();
curm++;
}
}
int main(){
for(int i=1;i<=65;i++){
for(int j=4;j<=65;j++){
fun(i,j);
}
}
int t;
cin>>t;
while(t--){
int n,m;
cin>>n>>m;
M = m;
for(int i=n;i>=1;i--){
pegs[1].push(i);
}
cout<<fun(n,m)<<endl;
solve(n,m,1,m);
while(pegs[m].size()){
pegs[m].pop();
}
}
return 0;
}