http://poj.org/problem?id=1006,大意是每个人在一生中都有三个生理循环:身体,情绪和智力。这三个生理循环的周期分别是23,28和33天。在这23,28和33天内,这三个生理循环都会有一个顶点,在顶点,人们在身体,情绪和智力方面都会有更好的表现。
因为这三个生理循环有不同的周期,所以他们到达顶点的时间往往也是不同的。现在要求找出这三个顶点同时出现的时间点。
输入:
一共有很多行,每行包含四个数字:p,e,i,d. p,e i分别表示今年出现的身体,情绪和智力顶点的日子(可能不是第一个),d表示给定的日期,可能比p,e,i要小。输入以p = e =i =d =-1结束。
输出:
输出下一个顶点日距离给定日子d的距离。以这样的形式:
Case 1:the next triple peak occurs in 1234 days.
这实际上是中国剩余定理的一个应用。中国剩余定理,又称孙子定理,取自<<孙子算经>>中的“物不知数”问题:有物不知其数,三个一数余二,五个一数余三,七个一数又余二,问该物总数几何?
就是说一个数用三除余二,用五除余三,用七除用余二,问这个数是几。
解法如下:
1.先找到35(5 * 7)的倍数,要求除三余1, 这里可以看出最小是70
2. 找到 21(3 * 7)的倍数,要求除五余1, 这里最小是21,
3. 找到15(3 * 5)的倍数,要求除7余1, 这里最小是15
用70 * 2 + 21* 3 + 15 * 2 = 233,233 %(3 * 5 * 7) = 23,23是最小的解,实际上23 + (3*5*7)*k ,k >= 0都是题目的解。
为什么这个算法是可行的呢? 对于三个两两互质的数字, i, j , k,我们有gcd(i,j) = gcd(i,k) = gcd(j,k) = 1. 我们令M1 = j * k, ,M2 = i * k, M3 = i * j,必然存在t1,t2,t3
- t1 * M1 = 1 (mod i)
- t2 * M2 = 1 (mod j)
- t3 * M3 = 1(mod k)
t1,t2,t3分别称作M1,M2,M3的逆元。
假如有一个数余i等于a1, 余j等于 a2, 余k等于 a3,那么有
- a1 * t1 * M1 = a1(mod i)
- a2 * t2 * M2 = a2(mod j)
- a3 * t3 * M3 = a3(mod k)
那么(a1 * t1 * M1 + a2 * t2 * M2 + a3 * t3 * M3) % i = (a1 * t1 * M1) % i + 0 + 0 = a1(因为M2和M3都是i的倍数,所以余i得0)
(a1 * t1 * M1 + a2 * t2 * M2 + a3 * t3 * M3) % j = 0 + (a2 * t2 * M2) % j + 0 = a1(因为M1和M3都是j的倍数,所以余j得0)
(a1 * t1 * M1 + a2 * t2 * M2 + a3 * t3 * M3) % k = 0 + 0 + (a3 * t3 * M3) % k = a1(因为M1和M2都是k的倍数,所以余k得0)
可以看出(a1 * t1 * M1 + a2 * t2 * M2 + a3 * t3 * M3) 是一个解。又因为 i * j * k是可以被i,j,k三者整除,所以
(a1 * t1 * M1 + a2 * t2 * M2 + a3 * t3 * M3) + (i * j * k)* n,n >= 0都是问题的解。
poj 1006考察的就是对这个定理的了解程度。所以剩下的问题就是如何求解t1, t2和t3使得
- t1 * M1 = 1 (mod i)
- t2 * M2 = 1 (mod j)
- t3 * M3 = 1(mod k)
这一般可由扩展欧几里得算法解除,对于本题,可以事先用暴力搜索找出这些值,t1 = 5544, t2 = 14421, t3 = 1428,剩下的问题就是套用剩余定理的公式了。
最后贴上accept的代码,仅供抛砖引玉之用:
#include <stdio.h> #define ALL_PRODUCTION (23 * 28 * 33) int main() { int p,e,i,d,k; for(int j = 0;;j++) { scanf("%d %d %d %d",&p,&e,&i,&d); if(p == -1) break; else { k = 5544 * p + 14421 * e + 1288 * i; k = k % ALL_PRODUCTION; k = k - d; if(k <= 0) { k += ALL_PRODUCTION; } printf("Case %d: the next triple peak occurs in %d days.\n",j + 1, k); } } return 0; }