运行要求
运行时间限制: 2sec
内存限制: 1024MB
原题链接
Strange Bank

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

  • 1块钱
  • 6块钱,6X6=36块钱,6X6X6=216块钱
  • 9块钱,9X9=81块钱,9X9X9=729块钱

请问,要从银行里取出N块钱的话,最少要取多少次钱
规定取出来的钱不能再度存入银行

输入前提条件

  • 1 <= N <= 100000
  • N为整数

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

N

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

例1
输入

127

输出

4

1块钱, 9块钱, 36(=6X6)块钱, 81(=9X9)块钱,
以上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)

AtCoder ContextABC 99 C.001.jpeg

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

AtCoder ContextABC 99 C.002.jpeg

AtCoder ContextABC 99 C.003.jpeg

比如说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

スクリーンショット 2020-03-08 17.15.19.png

当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的最小值

AtCoder ContextABC 99 C.005.jpeg

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

还有一个办法就是遍历从1到N,对每一个整数求出其最小的取法。对于1-5的整数i来说,最小的解法是i本身,对于i=6和i=9来说,最小的解法是1
首先把这些已知的信息存在一个数组memo
其他的整数可以根据前面索引为i-1,i-6,i-9等数组的值来进行计算
最后求出memo的最后一项值

这道题的方法还可以参照我之前写的一个爬楼梯的解法
AtCoder ContextABC 126 C Typical Stairs(经典爬楼梯问题)

代码

方法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])

总结

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

※ 另外,我会在我的微信个人订阅号上推出一些文章,欢迎关注
二维码.jpg



伟大不DIAO
1 声望1 粉丝

Done is better than perfect