vlambda博客
学习文章列表

AtCoder Context ABC 104 C Strange Bank(奇怪的银行)---动态规划解法

运行要求

运行时间限制: 2sec

内存限制: 1024MB


题目

有一家银行,为了让它的用户不那么容易取到钱,规定,每人每次只能取出如下金额的钱

1块钱

6块钱,6*6=36块钱,6*6*6=216块钱

9块钱,9*9=81块钱,9*9*9=729块钱

请问,要从银行里取出N块钱的话,最少要取多少次钱

规定取出来的钱不能再度存入银行


输入前提条件

1 <= N <= 100000

N为整数


输入

输入都以以下标准从命令行输入


N


输出

取出N块钱所需要取钱的最少次数x


例1

输入


127


输出


4


1块钱, 9块钱, 36(=6*6)块钱, 81(=9*9)块钱,

以上4次取法结束后,总共能够取出127块钱


例2

输入


3


输出


3


1块钱, 1块钱, 1块钱,

以上3次取法结束后,总共能够取出3块钱


例3

输入


44852


输出


16





读懂题目
可以看作一个整数N,要把它用1, 小于N的6的次方数, 小于N的9的次方数做拆分。找出拆分所用次数最少的情况。

解题思路

1. 每一种情况都加以统计,然后算出最小的值,显然是不太可能的。因为N最大值是100000,所以每一种都要统计出来话时间会非常大。

2. 每一个整数 I 都有一个对应的值X, X为取出I的钱所用的取钱次数的最小次数。

比如说I为1的话,那么X = 1 (1次重复取出1)
比如说I为2的话,那么X = 2 (2次重复取出1)
比如说I为3的话,那么X = 3 (3次重复取出1)
比如说I为4的话,那么X = 4 (4次重复取出1)
比如说I为5的话,那么X = 5 (6次重复取出1)




比如说I为6的话,那么X = 1 (1次取出6)


AtCoder Context ABC 104 C Strange Bank(奇怪的银行)---动态规划解法


AtCoder Context ABC 104 C Strange Bank(奇怪的银行)---动态规划解法


比如说I为7的话,那么X = 2 (取出6,再取出1)

当I为7的时候,有2种取法,

1. 一种是取出1,再处理剩下的6

2. 一种是取出6,再处理剩下的1

第1种取法所需要的数量是1(取出1需要1次) + 1(取出6最少需要1次) = 2 次

第2种的取法所需要的数量是1(取出6需要1次) + 1(取出1最少需要1次)

= 2 次

所以I为7的时候,最后的结果是min(第1种的取法,第2种的取法) = 2



当I为8的时候,有2种取法

1. 一种是取出1,再处理剩下的7

2. 一种是取出6,再处理剩下的2

第1种取法所需要的数量是1(取出1需要1次) + 2(取出7最少需要2次) = 3 次

第2种的取法所需要的数量是6(取出6需要1次) + 2(取出2最少需要2次) = 3 次

所以I为8的时候,最后的结果是min(第1种的取法,第2种的取法) = 3


当I为9的时候,有3种取法

1. 一种是取出1,再处理剩下的8

2. 一种是取出6,再处理剩下的3

3. 一种是取出9,再处理剩下的0

第1种的取法所需要的数量是1(取出1需要1次) +  3(取出8最少需要3次) = 4 次

第2种的取法所需要的数量是1(取出6需要1次) +  3(取出3最少需要3次) = 4 次

第3种的取法所需要的数量是1(取出9需要1次) +  0(取出0最少需要0次) = 4 次

所以I为9的时候,最后的结果是min(第1种的取法,第2种的取法,第3种的取法) = 1


当I为10的时候,有3种取法

1. 一种是取出1,再处理剩下的9

2. 一种是取出6,再处理剩下的4

2. 一种是取出9,再处理剩下的1

第1种的取法所需要的数量是1(取出1需要1次) +  1(取出9最少需要1次)

= 2 次

第2种的取法所需要的数量是1(取出6需要1次) + 4(取出4最少需要4次) = = 5 次

第3种的取法所需要的数量是1(取出9需要1次) +  1(取出1最少需要1次) = 2 次

所以I为9的时候,最后的结果是min(第1种的取法,第2种的取法,第3种的取法) = 2


我们隐约地可以感觉到,可以用递归的方法解决问题,因为对于整数N,拆分的方式的第一步的情况可以筛选出来

例如对于整数37

第1步去掉1的情况,所需要的最小次数是A

第1步去掉6的情况,所需要的最小次数是B

第1步去掉36的情况,所需要的最小次数是C

第1步去掉9的情况,所需要的最小次数是D

最后再求出A,B,C,D的最小值



方法1:用一个函数去求N的最小取钱次数。第1步去掉6的倍数,或者9的倍数,或者1以后,剩下的数的最小取钱次数可以再次套用这个函数。


方法2:还有一个办法就是遍历从1到N,对每一个整数求出其最小的取法。对于1-5的整数i来说,最小的解法是i本身,对于i=6和i=9来说,最小的解法是1

首先把这些已知的信息存在一个数组memo

其他的整数可以根据前面索引为i-1,i-6,i-9等数组的值来进行计算

最后求出memo的最后一项值


这道题的方法还可以参照我之前写的一个爬楼梯的解法




代码


方法1


import sys
sys.setrecursionlimit(20000000)
N = int(input())
memo = [-1 for i in range(110000)]
def calculate(n):

if n == 0:
memo[n] = 0
return 0

if memo[n] != -1:
return memo[n]

res = n

pow6Num = 1
while pow6Num*6 <= n:
pow6Num = pow6Num * 6
res = min(res, calculate(n - pow6Num) + 1)


pow9Num = 1
while pow9Num*9 <= n:
pow9Num = pow9Num * 9
res = min(res, calculate(n - pow9Num) + 1)

memo[n] = int(res)
return int(res)



result = calculate(N)
print(memo[N])


方法2


N = int(input())

def calculate(n):

memo = [0 for i in range(N+1)] # 0-N

for index in range(1,n+1):
pow6 = 6
pow9 = 9
if index < 6:
memo[index] = index
elif index == 6:
memo[index] = 1
elif index == 9:
memo[index] = 1
else:
values = []

while pow6 <= index:
values.append(1 + memo[index - pow6])
pow6 = pow6 * 6

while pow9 <= index:
values.append(1 + memo[index - pow9])
pow9 = pow9 * 9

values.append(1+memo[index-1])
minValue = min(values)

memo[index] = minValue

return memo

result = calculate(N)
print(result[N])





总结

这道题的解法有很多,我会在后续的文章中介绍别的解法
目前最常用的解法就是用动态规划的思想去解决