题意:
这题的目的是找到在三个循环周期内找到重合的天。
首先给定三个生理周期的出现的某一天,这样很自然能得到高峰是一个单独周期的第几个天。
p = p % 23;
e = e % 28;
i = i % 33;
我们假设是第S天满足要求,那么S必然满足以下方程式:
S = p + 23k1 = e + 28k2 = i + 33k3 (k1, k2, k3 均 ≥ 0, 且为正整数)
我们若是想要直接求出k1, k2, k3不太现实,我们可以由结果逆推:
S % 23 = p;
S % 28 = e;
S % 33 = i;
我们现在假设三个数n1, n2, n3,这三个数分别满足:
n1 % 23 = p;
n2 % 28 = e;
n3 % 33 = i;
我们的目标就是找到n1,n2,n3这三个数,使得 S = n1 + n2 + n3 满足 S = p + 23k1 = e + 28k2 = i + 33k3 (k1, k2, k3 均 ≥ 0, 且为正整数)
由数学公式:如果 a % b = c , 则有 (a + kb ) % b = c
例如由(n1 + n2 + n3) % 23 = p ==>(n2 + n3) % 23 = p ==> n2 % 23 = p, n3 % 23 = p, 即:
n1 % 23 = p且n2, n3为23的倍数。
进而对n1,n2,n3三种情况按照上述讨论,得到:
①n1 % 23 = p且n2, n3为23的倍数。
②n2 % 28 = e且n1, n3为28的倍数。
③n3 % 33 = i且n1, n2为33的倍数。
再进一步推导:
①n1 % 23 = p且为28、33的倍数。
②n2 % 28 = e且为23、33的倍数。
③n3 % 33 = i且为23、28的倍数。
到了现在,我们就能求出n1, n2, n3了,但是,还有一个小细节在这个地方,以①为例,我们并不是从所有的28,33的公倍数中找出%23 = p的数,而是先找出%23 = 1的数,再乘上k倍即可。
运用的数学公式为a%b=c,那么 (a*k)%b=kc
在最后,我们还是不能够保证我们算得的n1 + n2 + n3是满足条件的最小值,只需要通过%23,28,33的最小公倍数即可,理由还是,如果 a % b = c , 则有 (a + kb ) % b = c。
AC代码
#include <iostream>
using namespace std;
int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a % b);
}
int lcm(int a, int b)
{
return a / gcd(a, b) * b;
}
int main()
{
ios::sync_with_stdio(false);
int a, b, c, d, sum = 0;
while(cin >> a >> b >> c >> d && a != -1)
{
sum++;
a = a % 23; b = b % 28; c = c % 33;
int n1, n2, n3;
int flag1 = 1, flag2 = 1, flag3 = 1;
int s1 = lcm(28, 33), s2 = lcm(23, 33), s3 = lcm(23, 28);
for(int k = 1; flag1 || flag2 || flag3; k++)
{
if(flag1 && k * s1 % 23 == a)
{
flag1 = 0; n1 = k * s1;
}
if(flag2 && k * s2 % 28 == b)
{
flag2 = 0; n2 = k * s2;
}
if(flag3 && k * s3 % 33 == c)
{
flag3 = 0; n3 = k * s3;
}
}
int s = (n1 + n2 + n3) % 21252;
if(s == 0) s = 21252;
if(s - d < 0) s += 21252;
cout << "Case "<< sum << ": the next triple peak occurs in " << s - d << " days." << endl;
}
return 0;
}