分析:这道题的思路相对比较直接,最重要的优化技巧是要把已经计算过链条长度的数字缓存下来,从而加快计算之后数字链条长度的速度。题目中已经给出了若干数字,它们的链条长度是已知的,我们可以将其保存一个字典,字典的键为数字,对应的值为链条长度,例如169, 363601, 1454的链条长度都为三。

对每一个数字\(N\),我们建立一个数组\(arr\)保存其阶乘链条中的数。如果一个数\(a\)在上面的字典中已经出现了,则我们不需要再计算下去,直接用数组的长度加上数字\(a\)的链条长度即可。否则,我们检查数\(a\)是否在数组\(arr\)中已经出现过,如果已经出现过,则也可以停止计算,返回数组\(arr\)的长度即为数字\(N\)的的链条长度。如果以上两个条件都不满足,则我们计算数字\(a\)的各位数阶乘之和\(b\),并把\(b\)添加到数组\(arr\)中,继续循环。最后,我们在字典中添加数字\(N\)的链条长度。

另外一个优化技巧是,为了让我们在计算不同数字的链条长度可以共享同一个缓存字典,我们把这个字典设为全局变量,并在计算链条长度的函数中使用global关键字,使其可以修改字典这个全局变量,这样可以大大提高算法的效率。代码如下:

# time cost = 487 ms ± 2.44 ms

DT = {169:3,363601:3,1454:3,871:2,45361:2,872:2,45362:2,69:5,78:4,540:2}

def digit_fact_sum(x):
    fac = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
    res = sum([fac[int(i)] for i in str(x)])
    return res

def chain_length(n):
    arr,m = [],n
    global DT
    while True:
        if n in DT:
            length = len(arr) + DT[n]
            break
        elif n in arr:
            length = len(arr)
            break
        else:
            arr.append(n)
            n = digit_fact_sum(n)
    DT[m] = length
    return length

def main(N=10**6):
    c = 0
    for i in range(1,N):
        if chain_length(i) == 60:
            c += 1
    return c
12-16 10:35
查看更多