题目内容大意
题目大意:人有生理,心理以及智力的一个循环,分别经过23,28,33天一个循环到达顶峰。
输入:给你4个数,在年初的时候距离上一次的生理,心理,智力的顶峰的时间p,e,i。 以及这个年已经过了几天了d。
输出:计算下一次三项同时达到顶峰的时间。
个人想法:
这个问题一开始想通过算法解决,发现有点困难。去网上找了题解。说的是孙子定理或者是中国剩余定理,或者是中国余数定理。如果想看视频了解孙子定理,可以去西瓜视频上找李永乐老师讲《韩信点兵》的视频,李永乐老师讲孙子定理
PART1 孙子定理 如果孙子定理视频看明白了可以直接到PART2 到题目里的应用
《孙子定理解析》如果有不对的地方请指正
引入韩信点兵
韩信点兵的时候:
3人一排 余2人,5人一排 余3人 7人一排 余2人
问有多少兵。
一、列举法
(1)
除以3余2的数有:2,5,8,11,14,17,23,26,29...
除以5余3的数有:3,8,13,18,23,28...
除以7余2的数有:2,9,16,23,30...
可以找到他们三者共同的数为23,后面可能也有
(2)由于3,5,7三个数互质,所以他们的最小公倍数为 3*5*7 = 105
所以韩信的兵可能有 23 + 105 * n 其中n为自然数
二、孙子算经的方法
明代数学家的口诀:
三人同行七十稀,
五树梅花廿一枝,
七子团圆月正半,
除百零五便得知。
解释:
除以3的余数 * 70 + 除以5的余数 * 21 + 除以7的余数 * 15得到的和最后对105取余数就可以得到最小的人数。比如韩信点兵的人数 2 * 70 + 3 * 21 + 2 * 15 = 233 。233 对 105 取余数 得到 23 和列举法的答案一致
问题的关键就在于70,21,15这三个数是怎么得来的:
三个数3,5,7
分析:
70是5和7的倍数,但是70 mod 3 = 1
21是3和7的倍数,但是21 mod 5 = 1
15是3和5的倍数,但是15 mod 7 = 1
所以我们只要找到其中两个数的倍数但是对另外一个数取余得到1就可以了。这也可以解释为什么要这些数乘以余数,因为原来余数是1,如果要得到余数是3,那么这个数直接乘3就可以了,因为这个数是另外两个数的倍数不管乘以多少,对另外的两个数而言都是可以整除的。
PART2 题目解析
按照孙子定理23,28,33
23和28的最小公倍数是644,644 mod 33 = 17 因为17*2刚好等于34
644 * 2 = 1288 mod 33 = 1 所以33的余数 i 要乘1288
23和33的最小公倍数是759,759 mod 28 = 3,这个数要凑的比较多,3*19 mod 28 = 1
759*19 = 14421 所以28的余数 e 要乘14421
28和33的最小公倍数是924,924 mod 23 = 4 4*6 = 24 刚好比23多1
924 * 6 = 5544 所以23的余数 p 要乘以5544。
最后把结果套进题目。因为题目是当天不算的,所以最后如果得到的数是0或者是负数,还要加上他们3个数的最小公倍数,也就是三项同时到达顶峰的循环时间。题目已经告诉我们他们三者的最小公倍数是21252
最后的公式应该就是 (p * 5544 + e * 14421 + i * 1288 + 21252 - d) mod 21252
如果结果等于0的话,就再加一个21252。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
int main()
{
int p, e, i, d;
int nums = 0;
while(cin>>p>>e>>i>>d){
if (p == -1 && e == -1 && i == -1 && d == -1) break;
nums += 1;
int temp = (p * 5544 + e * 14421 + i * 1288 + 21252 - d) % 21252;
if (temp == 0) temp += 21252;
printf("Case %d: the next triple peak occurs in %d days.\n", nums, temp);
}
return 0;
}