ProjectEuler板刷记录

本文收录一些ProjectEuler中有趣的题目并给出个人题解。

Problem 54

本题并不困难,只是一个简单的模拟,但是如何让代码量减小是一个值得讨论的问题。

首先,我们可以将牌的点数和花色分开存储,我们只关心“所有牌的花色是否均相同”而不关注“每个牌的花色是多少”,所以可以分别排序。

之后,写一个函数level(card, color)来返回手牌的等级(数字越大,等级越高),若两人等级不同则直接判断,否则需要进一步比较具体点数。

这里是减少代码量的关键一步:不必对每一个等级都写一个具体的比较,注意到只有Flush的比较策略是不同的:每次拿剩下的最大手牌比较。

对于剩下的等级,我们只需对每个玩家存储:对每个k,出现过k次的牌的点数是什么。之后,依次比较出现过4次、3次、2次、1次的牌的点数大小即可。

Problem 59

简单有趣的一道题。可以通过字母频率分析猜一猜密钥,如果懒得做的话可以像我一样直接枚举。

得到所有可能的原文章后,我们要筛选出有意义的那一个,当然不能一个一个去看,可以在Sublime Text里搜索几个常见词汇,比如我搜索了the(两侧带空格),直接可以得到密钥是exp,问题就结束了。

Problem 60

本题是前60道题中难度最大的一道,但是也比较常规。

考虑转化到图论上。如果两个素数的拼接仍然是素数,就在它们之间连边,要求的就是数字和最小的 子图。这种东西就直接爆搜,远远跑不满五次方的上界,可以在 秒内跑出结果,如果用某些工程库的求 子图模板可以更快,但没必要。

本题中如果没有提前建好图,而是在爆搜过程中不断调用isprime会巨慢无比,什么东西都跑不出来,笔者最开始就是因为这个卡住了。

Problem 64

以连分数为背景的一道题,正好来学习一下相关内容。

定理:所有循环连分数都是二次无理数,且任何一个二次无理数都可以写成循环连分数的形式。

证明:先证明所有循环连分数都是二次无理数。对于一般的循环连分数 ,设 ,则

其中 都是分式线性变换,于是得到

右侧的复合仍然是一个分式线性变换,不妨设

则得到关于 的方程 ,因此循环连分数都是整系数二次方程的解,又因为无线连分数都是无理数,因此循环连分数表示了二次无理数。

接下来,我们证明任意二次无理数都可以写成循环连分数,这是对于本题重要的一步,因为我们可以通过这个过程得到计算连分数形式的算法。

首先,二次无理数总可以表示成 的形式,其中 都是整数且 。证明很容易,可以通分为

即可。

为什么要写成这样的形式?因为它的所有余项都有类似的形式:

其中 是整数并且 。其中,条件 保证了所有余项的分子中, 前面的系数都是

数学归纳求 的形式:设 ,那么

也有类似形式,带入得到

对比系数可得

实际应用时,可以用更简便的

根据 ,所以 确实都是整数,即 确实具有要求的形式。

到此,我们已经得到了求二次无理数的连分数形式的算法,下面证明余项只能取到有限多个值,因此必然存在循环节。

已经求得余项为

对于无理数,总有 ,同时,其共轭

因为 并且

因此对充分大的 ,必然有 ,因此

得到

,因此 只能取有限个值,进一步由递推公式, 也只能取有限个值,证毕。

代码就根据上面的递推写就行了,没什么难度。这里直接用了OI-wiki的实现。