题目链接:传送门
题目大意
在汉诺塔游戏中,有n个盘子在1号柱上,从
i
i
i号柱把一个盘子移动到
j
j
j号柱花费为
v
a
l
[
i
]
[
j
]
val[i][j]
val[i][j]。
现在需要求出把所有盘子从1号柱移到3号柱的最小花费。
观察发现,盘子编号小的一定不在编号大的的上方。
因此编号大的盘子不影响编号小的盘子的移动。
因此考虑使用记忆化搜索:
令
s
o
l
v
e
(
n
,
a
,
b
,
c
)
solve(n,a,b,c)
solve(n,a,b,c)表示前
n
n
n个盘子,从
a
a
a柱,经过
b
b
b柱,到达
c
c
c柱的最小花费。
最后的答案很显然,为
s
o
l
v
e
(
n
,
1
,
2
,
3
)
solve(n,1,2,3)
solve(n,1,2,3)。
思考怎样求出答案:
让前
n
−
1
n-1
n−1个盘子和第
n
n
n个盘子分别到达
c
c
c柱。
分两种情况:
第一种:先把前
n
−
1
n-1
n−1个盘子从
a
a
a挪到
b
b
b,
把第
n
n
n个盘子挪到
c
c
c,
然后把前
n
−
1
n-1
n−1个盘子从
b
b
b挪到
c
c
c。
此时的花费为
s
o
l
v
e
(
n
−
1
,
a
,
c
,
b
)
+
v
a
l
[
a
]
[
c
]
+
s
o
l
v
e
(
n
−
1
,
b
,
a
,
c
)
solve(n-1,a,c,b)+val[a][c]+solve(n-1,b,a,c)
solve(n−1,a,c,b)+val[a][c]+solve(n−1,b,a,c)。
第二种情况为:
先把前
n
−
1
n-1
n−1个盘子挪到
c
c
c,
再把第
n
n
n个盘子挪到
b
b
b,
把前
n
−
1
n-1
n−1个盘子挪到
a
a
a,
把第
n
n
n个盘子挪到
c
c
c柱,
最后把前
n
−
1
n-1
n−1个盘子挪到
c
c
c柱。
此时花费为
s
o
l
v
e
(
n
−
1
,
a
,
b
,
c
)
+
v
a
l
[
a
]
[
b
]
+
s
o
l
v
e
(
n
−
1
,
c
,
b
,
a
)
+
v
a
l
[
b
]
[
c
]
+
s
o
l
v
e
(
n
−
1
,
a
,
b
,
c
)
solve(n-1,a,b,c)+val[a][b]+solve(n-1,c,b,a)+val[b][c]+solve(n-1,a,b,c)
solve(n−1,a,b,c)+val[a][b]+solve(n−1,c,b,a)+val[b][c]+solve(n−1,a,b,c)。
对两个花费取 m i n min min,并赋值给 d p [ n ] [ a ] [ b ] [ c ] dp[n][a][b][c] dp[n][a][b][c]即可。
代码:
#include<stdio.h>
#include<cstring>
#include<algorithm>
#define re register int
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=105;
const int INF=1e9;
ll val[5][5],dp[Size][5][5][5];
ll solve(int n,int a,int b,int c) {
if(!n) return 0;
if(dp[n][a][b][c]) return dp[n][a][b][c];
ll sum1=solve(n-1,a,c,b)+val[a][c]+solve(n-1,b,a,c);
ll sum2=solve(n-1,a,b,c)+val[a][b]+solve(n-1,c,b,a)+val[b][c]+solve(n-1,a,b,c);
return dp[n][a][b][c]=min(sum1,sum2);
}
int main() {
for(re i=1; i<=3; i++) {
for(re j=1; j<=3; j++) {
val[i][j]=read();
}
}
int n=read();
printf("%lld",solve(n,1,2,3));
return 0;
}