【Poj 1006】生理周期 Biorhythms
今日学校作业中有这道题目,好蛋在这里提供三种解题方法呦~
题目:人生来有体力、感情、智力三个生理周期,周期长度分别为23天、28天、33天,各周期有一天高峰,通常三高峰不同日(a,b,c)。任务是给定从当年第一天起的一个天数(d),求从该给定时间d(不包括此时间)开始,下一次三高峰同天的时间(距给定时间的天数)。
分析数据:(周期:23 28 33)
a b c d
4 5 6 7
当a输入为4的时候,由于周期为23,则第27(4+23)天也是体力高峰天,第50(4+23+23)也是体力高峰天........ ........第4+23x也是体力高峰天。
同理,当b输入为5的时候,5+28y也是感情高峰天。
同理,当c输入为6的时候,6+33z也是智力高峰天。
由于该题目想让我们求三高峰同天的时间,即4+23x=5+28y=6+33z=h;输出距给定时间的天数,即输出h-d。
法一:普通循环。
注解:首先我们从第8(7+1)天开始找,找到第一个符合(h-4)%23=0的天数;然后在这个基础上,调整h,使得天数满足(h-5)%28=0,每次调整的时候,h=h+23(因为h已经满足(h-4)%23=0);之后继续调整 h,使它同时满足 (h - 6) % 33 = 0,每次调整时,将 h 增加 23×28(因为这是前两个周期的最小公倍数)。
点击查看代码
#include <stdio.h>
int main()
{
int a, b, c, d, count = 0, h;
while (scanf("%d%d%d%d", &a, &b, &c, &d) && a != -1)
{
count++;
for (h = d + 1; (h - a) % 23 != 0;)
{
h++;
}
while ((h - b) % 28 != 0)
{
h += 23;
}
while ((h - c) % 33 != 0)
{
h += 23 * 28;
}
printf("Case %d: the next triple peak occurs in %d days.\n", count, h - d);
}
return 0;
}
法二:数组法。
注解:我们输入a,b,c,d数据之后,a,b,c中的最小值不断增加相应周期的长度,直到a,b,c三个数字相等,即为这三个高峰的相同日;此时再减去d,则求出距离给定时间的天数。
分析第一组数据:(周期:23 28 33)
a b c d
4 5 6 7
(4,5,6)其中4最小,则4+23=27.
第1次结果:(27,5,6)其中5最小,则5+28=33.
第2次结果:(27,33,6)其中6最小,则6+33=39.
第3次结果:(27,33,39)其中27最小,则27+23=50.
第4次结果:(50,33,39)其中33最小,则33+28=61.
第5次结果:(50,61,39)其中39最小,则39+33=72.
........ ........
(303,285,303)其中285最小,则285+28=313.
(303,313,303)其中303最小,则303+23=326.
........ ........
第1858次结果:(16978,16973,16968)其中16968最小,则16968+33=17001.
第1859次结果:(16978,16973,17001)其中16973最小,则16973+28=17001.
第1860次结果:(16978,17001,17001)其中16978最小,则16978+23=17001.
第1861次结果:(17001,17001,17001)
则三高峰同天的时间为17001,距离给定天数7相差16994,即输出16994.正确。
分析第二组数据:(周期:23 28 33)
a b c d
241 302 350 10
241 / 23 = 10......11,
302 / 28 = 10......22,
350 / 33 = 10......20.
(a+23x=b+28y=c+33z=h)
我们根据之前的公式a+23x,b+28y,c+33z,得a=11,b=22,c=20,这也就是体感智第一次高峰出现的时间。此时我们根据题意:11+23x=22+28y=20+33z=h,很容易得出当x=9,y=7,z=6的时候h=218,即输出为h-r=208.
但若我们按照刚才的思路进行计算,则
(241,302,350)其中241最小,则241+23=264.
第1次结果:(264,302,350)其中264最小,则264+23=287.
第2次结果:(287,302,350)其中287最小,则287+23=310.
........ ........
........ ........
第2316次结果:(21447,21442,21437)其中21437最小,则21437+33=21470.
第2317次结果:(21447,21442,21470)其中21442最小,则21442+28=21470.
第2318次结果:(21447,21470,21470)其中21447最小,则21447+23=21470.
第2319次结果:(21470,21470,21470)
则三高峰同天的时间为21470,距离给定天数10相差21460,即输出21460.错误。
启发1:由此我们收到启发,我们输入a,b,c数据之后,要先求出体感智第一次高峰出现的时间,然后在此基础上对体感智第一次高峰出现的时间不断累加对应的周期,直到三个数相等并且大于给定时间d。
分析第三组数据:(周期:23 28 33)
a b c d
8 8 8 6
对于该数据,我们很容易就观察到体感智在第8天达到共同的高峰,此时输出为2(8-6).
但若我们按照刚才的思路进行计算,则
(8,8,8)其中8最小,则8+23=31.
第1次结果:(31,8,8)其中8最小,则8+28=36.
第2次结果:(31,36,8)其中8最小,则8+33=41.
........ ........
........ ........
第2324次结果:(21237,21232,21227)其中21227最小,则21227+33=21260.
第2325次结果:(21237,21232,21260)其中21232最小,则21232+28=21260.
第2326次结果:(21237,21260,21260)其中21237最小,则21237+23=21260.
第2327次结果:(21260,21260,21260)
则三高峰同天的时间为21260,距离给定天数6相差21254,即输出21254.错误。
启发2:由此我们受到启发,当我们输入数据a,b,c,d之后,要先判断a,b,c是否相等并且a,b,c的值是否大于d,若a,b,c的值相等并且大于d,则我们直接输出a-d即可。
点击查看代码
#include<stdio.h>
int min(int haha[3])
{
int m = 0;
if (haha[1] < haha[0])
{
m = 1;
}
if (haha[2] < haha[m])
{
m = 2;
}
return m;
}//寻找数组中的最小值
int main()
{
int didi;
int a, b, c;
int d;
int count = 0;
int haha[3], hehe[3] = { 23,28,33 };
while (scanf("%d %d %d %d", &a, &b, &c, &d))
{
if ((a == -1) && (b == -1) && (c == -1) && (d == -1))
{
break;
}
else
{
haha[0] = a % 23;
haha[1] = b % 28;
haha[2] = c % 33;//启发一
count++;
while (1)
{
if((haha[0]==haha[1])&&(haha[1]==haha[2])&&(haha[0])>d)
{
didi = haha[0]-d;
break;
}//启发二
int q = min(haha);
haha[q] = haha[q] + hehe[q];
if ((haha[0] == haha[1]) && (haha[1] == haha[2]) && (haha[0] > d))
{
didi = haha[0] - d;
break;
}
}
}
printf("Case %d: the next triple peak occurs in %d days.\n", count, didi);
}
return 0;
}
法三:运用中国剩余定理求解。
分析数据:(周期:23 28 33)
a b c d
4 5 6 7
也就是说,我们要找到一个数h,使得h % 23 = 4,h % 28 = 5,h % 33 = 6,具体的解法分为三步:(4+23x=5+28y=6+33z=h)
1.从23与28的公倍数中找到被33除余1的最小数为1288,从23与33的公倍数中找到被28除余1的最小数为14421,从28与33的公倍数中找到被23除余1的最小数为5544。
2.用12886(6为最终结果除以33的余数),144215(5为最终结果除以28的余数),55444(4为最终结果除以23的余数),然后把这3个乘积相加,12886+144215+55444=102009。
3.用102009除以23,28,33的最小公倍数21252,得到余数17001,该余数就是符合条件的最小数。
原理1:我们知道一个基本的数学定理:如果有a%b=c,则有(a+kb)%b=c(k为非零整数),换句话说,如果一个除法运算的余数为c,那么被除数与k倍的除数相加(或相减)的和(差)再与除数相除,余数不变。
原理2:我们知道一个基本的数学定理:如果a%b=c,那么(a*k)%b=a%b+a%b+…+a%b=c+c+…+c=kc(k>0),也就是说,如果一个除法的余数为c,那么被除数的k倍与除数相除的余数为kc。
原理3:我们知道一个基本的数学定理:如果有a%b=c,则有(a-kb)%b=c(k为非零整数),换句话说,如果一个除法运算的余数为c,那么被除数与k倍的除数相加(或相减)的和(差)再与除数相除,余数不变。
注解:我们首先假设n1是满足除23余4的一个数,也就是满足23x+4(x>=0)的一个数,同样,我们假设n2是满足除28余5的一个数,假设n3是除33余6的一个数。从n1的角度而言,如果n2是23的倍数,n1+n2就依然满足除以23余4。同理,如果n3也是23的倍数,那么n1+n2+n3的和就满足除以23余4。从n2的角度而言,如果n1是28的倍数,则n1+n2依然满足除28余5,如果n3也是28的倍数,则n1+n2+n3依然满足除28余5。n3同理(原理1)。
则我们可以归纳出:
1.为使n1+n2+n3的和满足除以23余4,n2和n3必须是23的倍数。
2.为使n1+n2+n3的和满足除以28余5,n1和n3必须是28的倍数。
3.为使n1+n2+n3的和满足除以33余6,n1和n2必须是33的倍数。
也就是:
n1除以23余4,且是28和33的公倍数。
n2除以28余5,且是23和33的公倍数。
n3除以33余6,且是23和28的公倍数。
所以,这个问题解法的本质是从28和33的公倍数中找一个除以23余4的数n1,从23和33的公倍数中找一个除以28余5的数n2,从23和28的公倍数中找一个除以33余6的数n3,再将三个数相加得到解。
在求n1,n2,n3时又用了一个小技巧,以n1为例,并非从28和33的公倍数中直接找一个除以23余4的数,而是先找一个除以23余1的数,再乘以4。此时n1+n2+n3=102009是满足题意的一个解,但不是最小解(原理2)。
最后第三步,当我们用102009(h)除以23、28、33的公倍数21252时,我们实际上是在寻找一个更小的数,这个数乘以21252后,再加上特定的余数,能够满足原来的条件。h既是23的倍数加4,又是28的倍数加5,还是33的倍数加6,为了找到这样一个数,需要考虑23、28、33的公倍数。因为只有公倍数才能同时被这三个数整除,从而有可能通过加上相应的余数来满足所有条件。
也就是说:102009=21252×4+17001=4+23×4435=5+28×3643=6+33×3091,
等式两侧同时减去4×21252,得:17001=4+739×23=5+607×28=6+515×33.(最大限度减去,没法再减了,21252=924×23=759×28=644×33,没法再减了)
注:对于解题步骤中的标黑数字,我们可以通过for循环或者乘法逆元(扩展欧几里得算法)求出。
点击查看代码
#include<stdio.h>
int main()
{
for (int i = 0; i >= 0; i++)
{
if (i % 23 == 1 && i % (28 * 33) == 0)
{
printf("%d", i);
break;
}
}
return 0;
}
点击查看代码
#include <stdio.h>
int main()
{
int a, b, c, d;
int count = 1;
while (1)
{
scanf("%d %d %d %d", &a, &b, &c, &d);
if (a == -1 && b == -1 && c == -1 && d == -1)
{
break;
}
int n = (5544 * a + 14421 * b + 1288 * c) % 21252;
n = (n - d + 21252) % 21252; //0 0 0 10
//由于取模运算可能导致负数结果(如果n < d),因此加上21252确保结果是非负的
if (n == 0)
{
n = 21252;
}//0 0 0 0
//如果经过上述计算后n等于0,这意味着给定的天数d本身就是一个峰值日(即同时是23、28和33的倍数)。然而,题目要求的是下一个峰值日,因此需要将n设置为21252,表示下一个完整的周期后的峰值日(即23、28、33的公倍数)。
//10 10 10 10 10+23*x=10+28*y=10+33*z=h 23*x=28*y=33*z=(h-10)=21252
printf("Case %d: the next triple peak occurs in %d days.\n",count ,n);
count++;
}
return 0;
}
中国剩余定理的简单应用:


好哒,今天的笔记就整理到这里辣,有什么问题也欢迎指出,阿里嘎多!!!
浙公网安备 33010602011771号