最近我參加了 @kenchung 的「數學 × 程式編寫比賽 (第八回)」,原贴地址:@kenchung/question-mathematics-programming-competition-8
为了拼个奖项,最初解决这个问题的时候,我先用最熟悉的C#语言完成,贴子在这里:@speeding/question-mathematics-programming-competition-8
程序不难,但我总感觉源程序写得太啰嗦,应该有更简练的解法,在乘坐动车回家的路上,我琢磨出来了具体的思路,这次使用的语言是强大的Python。强大到什么程度?我把它弄成了一行语句,直接先看这一条疯狂的语句吧。
print(int(sum(map(lambda d: 0.5 if d != ''.join(map(lambda i:'012xx59x86'[int(i)], d[::-1])) and ''.join(map(lambda i:'012xx59x86'[int(i)], d[::-1])) in ['%04d' % x for x in range(0, 10000)] else 1 , ['%04d' % x for x in range(0, 10000)]))))
程序可直接在python 3的解释环境中运行,不过为了压缩在一行里,代码是无法直接读懂了,运行效率也下降了许多倍,但结果仍是8824。
实际上用五行代码能够清晰地表示出编程的总体思路,如果对Python比较熟悉,代码并不难看懂。
def r(i) : return '012xx59x86'[int(i)]
def rot(dddd) : return ''.join(map(r, dddd[::-1]))
cards = ['%04d' % x for x in range(0, 10000)]
f = lambda d: 0.5 if (d!=rot(d) and rot(d) in cards) else 1
print(int(sum(map(f , cards))))
简单地解释一下算法和代码。
第一行语句:单个数字的旋转结果
def r(i) : return '012xx59x86'[int(i)]
即返回一个从0到9的数字旋转180度之后的结果。
r('1')的结果为'1'
r('3')的结果为'x',表示旋转后的结果不是数字。
第二行语句:四位数的旋转结果
def rot(dddd) : return ''.join(map(r, dddd[::-1]))
假设dddd为四位数,dddd[::-1]是python中特有的一种语法,可以将列表反序。这里运用一个map()函数,可以轻松地得到四位数旋转180度之后的结果。当然,如果旋转后的数无意义,则结果中肯定含有字符'x'。
rot('0159')的结果为'6510'
rot('1234')的结果为'xx21'
第三行语句:从'0000'到'9999'的所有卡纸
cards = ['%04d' % x for x in range(0, 10000)]
这一条语句中运用了python中的列表推导,'%04d' % x 可以把不足4位的数值前面补零,这样159就变成了'0159'
第四行语句:针对卡纸情况计数
f = lambda d: 0.5 if (d!=rot(d) and rot(d) in cards) else 1
这一条语句稍微难懂一些了。卡纸旋转180度后有几种情况:
旋转后仍为自己的,例如'1221',旋转后仍为'1221',计数1
旋转后无意义的,例如'1234',旋转后不是数字,计数也是1
旋转后为一个新的数值,例如'0159'旋转后为'6510',这两个数字可用一张卡纸表示。这里用了一个小技巧,我把两个卡纸的计数结果都设为0.5,这样合在一起就是一张卡纸,为了以后可以使用map()函数。
为了用一条语句表示if的逻辑,这里用了lambda表达式,意思是:
如果一个四位数旋转之后是一个在0000~9999的数值,并且不等于自身,就返回0.5,其它都返回1。
第五条语句:汇总统计结果
print(int(sum(map(f , cards))))
这里用map()函数把0到9999的卡纸全部计算一遍,求和就是最后的结果了,打印出来最后的整数值为8824。