# P11: 方阵中的最大乘积

Largest product in a grid

有一个 方阵中, 在这个方阵中, 四个在同一方向(从下至上, 从上至下, 从右至左, 从左至右或者对角线)上相邻的数的乘积最大是多少?

似乎只能暴力遍历, 练习一下 Import, 然后就变成了个矩阵.

一共有四个方向要遍历, 横向, 竖向, 主对角方向和副对角方向. 所以我们考虑的是把所有的行啊, 列啊, 对角线啊, 全部抽出来合并成一个列表.

这样就变成了长短不一的小列表的列表了. 然后每个小列表的最大乘积这个之前第 8 题已经搞定了. 分别求出最大值然后求这些最大值中的最大值就行了.

input = "\
08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08
49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65
52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91
22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80
24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50
32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70
67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21
24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72
21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95
78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92
16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57
86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58
19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40
04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66
88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69
04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36
20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16
20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54
01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48";
data1 = ImportString[input, "Table"];(* 横向列表 *)
data2 = Reverse /@ Transpose[data1];(* 竖向列表 *)
{data3, data4} = Table[Diagonal[#, k], {k, -16, 16}]& /@ {data1, data2};(* 主副对角线 *)
p8 = Max@Times@@@Partition[#, 4, 1]&;(* 第 8 题写过的拿过来 *)
Max[p8 /@ Join[data1, data2, data3, data4]](* 全部组合起来 *)

# P12: 高度可约的三角形数

Highly divisible triangular number

三角形数数列是通过逐个加上自然数来生成的.三角形数数列的前十项分别是:

让我们列举出前七个三角形数的所有约数:

1: 1
3: 1,3
6: 1,2,3,6
10: 1,2,5,10
15: 1,3,5,15
21: 1,3,7,21
28: 1,2,4,7,14,28

我们可以看出, 28 是第一个拥有超过 5 个约数的三角形数.

第一个拥有超过 500 个约数的三角形数是多少?

多用用 F1, 早点认识 DivisorSigma 这个函数, 这是个积性函数, 后面很多数论题都要用到这个.

虽然有数论解法, 不过我们还是先用穷举好了. 一般这种题后面还会出现无法穷举解决的加强型问题.

Again, 永远记住你的时间远比计算机的时间值钱.

NestWhile 还记得不, 我让你记住的来着.

NestWhile[# + 1&, 1, DivisorSigma[0, # (# + 1) / 2] < 500&];# (# + 1) / 2&@%

# P13: 大和

Large sum

计算出一百个 50 位数的和的前十位数字.

好吧我直接复制 import 30s 就搞定了, 不过我还是觉得我得教点什么, 就教点网页读取与处理吧, 就像这样:

input = URLExecute["https://projecteuler.net/problem=13", "Table"][[58 ;; -17]];
data = StringPartition[StringDelete[StringJoin@Flatten[input], {"<br/>", "</div>"}], 50];
IntegerDigits[Total[ToExpression /@ data]] ~ Take ~ 10 // FromDigits

# P14: 最长考拉兹序列

Longest Collatz sequence

在正整数集上定义如下的迭代序列:

n → n / 2  (若 n 为偶数)
n → 3n + 1 (若 n 为奇数)

从 13 开始应用上述规则, 我们可以生成如下的序列:

可以看出这个序列(从 13 开始到 1 结束)共有 10 项.尽管还没有被证明, 但我们普遍认为, 从任何数开始最终都能迭代至 1("考拉兹猜想").

从小于一百万的哪个数开始, 能够生成最长的序列呢?

注: 序列开始生成后允许其中的项超过一百万.

本题不建议用 Mathematica 做, 因为是硬计算.

<< ExampleData/Collatz.m
Table[Length@Collatz[i], {i, 10^4 - 1}]

算了将近 4 秒钟的时候我就感觉不对劲了, 不用编译药丸...

CollatzLength=Compile[{{x,_Integer}},Module[{c,n},
(For[n=x;c=1,n!=1,c+=1,If[EvenQ[n],n=Round[n/2],n=3*n+1]]);c],
CompilationTarget->"C",RuntimeAttributes->{Listable}];
Ordering[CollatzLength[Range[10^6]],-1]

# P15: 网格路径

Lattice paths

从一个 方阵的左上角出发, 只允许向右或向下移动, 则恰好有 条通往右下角的路径.

对于 方阵来说, 这样的路径有多少条?

呃, 高中数学排列组合, 不是很想解释...

Binomial[40, 20]

# P16: 幂的数字和

Power digit sum

, 而 的各位数字之和是 .

的各位数字之和是多少?

多用 F1.

Total@IntegerDigits[2^1000]

# P17: 表达数字的英文字母计数

Number letter counts

如果把 1 到 5 写成英文单词, 分别是:one, two, three, four, five, 这些单词一共用了 3 + 3 + 5 + 4 + 4 = 19 个字母.

如果把 1 到 1000 都写成英文单词, 一共要用多少个字母?

注意: 不要算上空格和连字符.例如, 342(three hundred and forty-two)包含 23 个字母, 而 115(one hundred and fifteen)包含 20 个字母.单词 "and" 的使用方式遵循英式英语的规则.

如果不懂英语语法的话... 跳过吧... 没啥意思...

IntegerName 并不符合语法. 答案里这个函数我抽了出来整合进了程序包, 我后来把语法扩展到了 10^72 以下, 一般认为 10^6 以下应该读自然语法, 10^9 以上应该读科学记法 (无 and).

不过这个并没有什么软用, 毕竟有 Wolfram Alpha.

words[x_] := Nest[StringReplace[#, n : (DigitCharacter..) :> WolframAlpha["spell" <> n, {{"Result", 1}, "Plaintext"}]]&, ToString@x, 2]
words[123456789101112]

正常解法:

AusEnglish1 = {"", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
AusEnglish2 = {"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"};
AusEnglish3 = {"twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};
EnglishToNumber[s_String] := StringLength@s;
NumberToEnglish[n_Integer /; 1 <= n <= 19] := StringDelete[(AusEnglish1 ~ Join ~ AusEnglish2)[[n + 1]], "-"]
AusEnglish4 = {"", "-one", "-two", "-three", "-four", "-five", "-six", "-seven", "-eight", "-nine"};
NumberToEnglish[n_Integer /; 20 <= n <= 99] := AusEnglish3[[Floor[n, 10] / 10 - 1]] <> AusEnglish4[[Mod[n, 10] + 1]]
NumberToEnglish[n_ /; 100 <= n <= 999 && Mod[n, 100] === 0] := AusEnglish1[[Floor[n, 100] / 100 + 1]] <> "hundred";
NumberToEnglish[n_Integer /; 100 <= n <= 999] := AusEnglish1[[Floor[n, 100] / 100 + 1]] <> "hundred and" <> NumberToEnglish[Mod[n, 100]]
NumberToEnglish[1000] := "one thousand";
StringLength@StringDelete[StringJoin@Table[NumberToEnglish[i], {i, 1, 1000}], {"", "-"}]

# P18: 最大路径和 I

Maximum path sum I

从下面展示的三角形的顶端出发, 不断移动到在下一行与其相邻的元素, 能够得到的最大路径和是 23.

3
7 4
2 4 6
8 5 9 3

如上图, 最大路径和为 3 + 7 + 4 + 9 = 23.

求从下面展示的三角形顶端出发到达底部, 所能够得到的最大路径和:

75
95 64
17 47 82
18 35 87 10
20 04 82 47 65
19 01 23 75 03 34
88 02 77 73 07 63 67
99 65 04 28 06 16 70 92
41 41 26 56 83 40 80 70 33
41 48 72 33 47 32 37 16 94 29
53 71 44 65 25 43 91 52 97 51 14
70 11 33 28 77 73 17 78 39 68 17 57
91 71 52 38 17 14 91 43 58 50 27 29 48
63 66 04 68 89 53 67 30 73 16 69 87 40 31
04 62 98 27 23 09 70 98 73 93 38 53 60 04 23

注意: 在这个问题中, 由于只有 16384 条路径, 通过尝试所有的路径来解决问题是可行的.但是, 对于第 67 题, 虽然是一道相同类型的题目, 但是三角形将拥有一百行, 此时暴力破解将不能解决, 而需要一个更加聪明的办法

讲真, 这个聪明的办法并不难想, 对于上层的每一个数都选下层较大的那个数走好了.

解释下 state 的结果是选择不同路径的结果, 前一个是往左走的和, 后一个是往右走的和.

choose 就是取结果大的那个, FoldPair 就是两层两层的比较的意思, FoldPair 是个二元运算符.

input = ImportString["
75
95 64
17 47 82
18 35 87 10
20 04 82 47 65
19 01 23 75 03 34
88 02 77 73 07 63 67
99 65 04 28 06 16 70 92
41 41 26 56 83 40 80 70 33
41 48 72 33 47 32 37 16 94 29
53 71 44 65 25 43 91 52 97 51 14
70 11 33 28 77 73 17 78 39 68 17 57
91 71 52 38 17 14 91 43 58 50 27 29 48
63 66 04 68 89 53 67 30 73 16 69 87 40 31
04 62 98 27 23 09 70 98 73 93 38 53 60 04 23", "Table"]
state = {PadLeft[#1, Length@#1 + 1] + #2, PadRight[#1, Length@#1 + 1] + #2}&;
choose[a_, b_] := Max@@@Transpose@state[a, b];
Max@FoldPair[{choose[#1, #2], choose[#1, #2]}&, input]

# P19: 数星期日

Counting Sundays

下列信息是已知的, 当然你也不妨自己再验证一下.

1900 年 1 月 1 日是星期一.

三十天在九月中, 四六十一也相同.

剩下都是三十一, 除去二月不统一.

二十八天平常年, 多加一天在闰年.

闰年指的是能够被 4 整除却不能被 100 整除的年份, 或者能够被 400 整除的年份.

在二十世纪(1901 年 1 月 1 日到 2000 年 12 月 31 日)中, 有多少个月的 1 号是星期天?

F1....... 不想在这里深入讲, 想自己算的话, 自行搜索日期计数.

Count[DateRange[{1901, 1, 1}, {2000, 12, 31}, Sunday], {_, _, 1}]

# P20: 阶乘数字和

Factorial digit sum

的意思是

例如, , 所以 10! 的各位数字和是 .

求出 的各位数字和.

和 16 题又有什么区别呢?

Total@IntegerDigits[100!]

连续计时7分41秒,P14莫名被坑....P17未计时.