题目链接:CodeForces - 392B
题意:在经典的汉诺塔的基础上进行了改造,不是问最少移动次数,而是问最少移动代价;从i柱移动到j柱的代价是t[i][j],求最少移动代价;
思路:先回顾一下汉诺塔,
当有n个盘子时最少需要移动(2^n)-1次;路径打印如下:
#include
using namespace std;
void dfs(int n, int from, int by, int to){
if(n==1){
printf("%d->%d\n", from, to);
return;
}
dfs(n-1, from, to, by);
printf("%d->%d\n", from, to);
dfs(n-1, by, from, to);
}
int main(){
int n;
while(cin >> n){
dfs(n, 1, 2, 3);
}
return 0;
}
当只有一个盘子时,直接将盘子移动到3号柱,图一;
有两个盘子时,先将小盘移到2号柱,再将大盘移到3号柱,再将小盘移动到3号柱, 图二;
当盘子个数为n时,把n-1看作是一个整体,把n-1个先挪到二号柱,再把第n个挪到3号柱,再把n-1挪到3号柱;这其实就是一个递归的过程;
------------------------------------------------------------------------华丽的分割线-------------------------------------------------------------------------------
现在再来看看这道题,汉诺塔的最小代价;
其实汉诺塔无论怎么挪,都只有两种方式:
一:同找最少次数相同;先把n-1个盘子经过3号柱的辅助挪到2号柱,再把第n个盘子挪到3号柱, 最后把前n-1个盘子经由1号柱的辅助挪到3号柱;此时dfs(n, from, by, to)=dfs(n-1, from, to, by)+t[from][to]+dfs(n-1, by, from, to);
二:先把n-1个盘子经由2号柱的辅助挪到3号柱,再把第n个盘子挪到2号柱,再把前n-1个盘子从3号柱经由2号柱的辅助挪到1号柱,再把第n个盘子从2号柱挪到3号柱,最后把前n-1个盘子从1号柱经由2号柱的辅助挪到3号柱;此时dfs(n, from, by, to)=dfs(n-1, from, by, to)+t[from][by]+dfs(n-1, to, by, from)+t[by][to]+dfs(n-1, from, by, to);
#include
using namespace std;
typedef long long ll;
ll t[4][4], dp[50][4][4][4];
ll dfs(int n, int from, int by, int to){
if(n==1) return min(t[from][to], t[from][by]+t[by][to]);
if(dp[n][from][by][to]!=-1) return dp[n][from][by][to];//如果此状态已经有结果,就直接返回,优化时间;
ll temp1, temp2;
temp1=dfs(n-1, from, to, by)+t[from][to]+dfs(n-1, by, from, to);
temp2=dfs(n-1, from, by, to)+t[from][by]+dfs(n-1, to, by, from)+t[by][to]+dfs(n-1, from, by, to);
dp[n][from][by][to]=min(temp1, temp2);
return dp[n][from][by][to];
}
int main(){
for(int i=1; i<=3; i++){
for(int j=1; j<=3; j++){
scanf("%lld", &t[i][j]);
}
}
int n;
scanf("%d", &n);
memset(dp, -1, sizeof(dp));
printf("%lld\n", dfs(n, 1, 2, 3));
return 0;
}