0259 可达数
* * * *
拉格朗日计划
* * * *
可达数

若一个自然数能表示成符合下列规则的算术表达式的结果,就称其为可达数:

  • 按从小到大的顺序使用数字1至9恰好各一次。

  • 相邻数字可以连起来使用,长度不限(例如,将数字2、3、4连接起来得到234)。

  • 只能使用四则运算(加减乘除),每种运算使用数量不限,也可以不用。

  • 可以使用(包括嵌套使用)任意数量的括号。

例如,42是可达数,因为有$(1/23) \times ((4\times 5)-6) \times (78-9)=42$。

求所有可达数之和。

本题难度:



解答

本题是区间动态规划问题。把数字a到数字b之间可以形成的可达数记作集合$s(a,b)$,则需要求$s(1,9)$。

$s(a,b)$可以用这样的方式计算:遍历$[a,b)$之中的每个整数c,从$s(a,c)$和$s(c+1,b)$中各取一个数$u,v$,并将$u+v,u-v,uv,u/v$添加到$s(a,b)$中。

对于除法需要注意v不能为0,不过这一条件事实上可以进一步优化为$v$既不能太小也不能太大,否则无法再通过乘除法得到整数,此处取的上下界是$10^{\pm5}$。

最终结果是$20101196798$。

注:为便于作浮点数除法,以下代码为Python 3。

import math
epsilon=0.00001

s=[[set([float("0123456789"[i:j+1])]) if j>=i else set() for j in range(10)] for i in range(10)]

for x in range(2,10):
    for a in range(1,11-x):
        b=a+x-1
        for c in range(a,b):
            for u in s[a][c]:
                for v in s[c+1][b]:
                    s[a][b].add(u+v)
                    s[a][b].add(u-v)
                    s[a][b].add(u*v)
                    if epsilon<math.fabs(v)<100000:
                        s[a][b].add(u/v)

print(sum(set(round(x) for x in s[1][9] if math.fabs(x-round(x))< epsilon and round(x)>0)))