POJ 1006 中国剩余定理

本文通过一道关于人体生理周期的问题,介绍了如何运用中国剩余定理解决这类问题,并给出了详细的数学推导过程及C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

POJ1006: 中国剩余定理的完美演绎

问题描述
人自出生起就有体力,情感和智力三个生理周期,分别为23,28和33天。一个周期内有一天为峰值,在这一天,人在对应的方面(体力,情感或智力)表现最好。通常这三个周期的峰值不会是同一天。现在给出三个日期,分别对应于体力,情感,智力出现峰值的日期。然后再给出一个起始日期,要求从这一天开始,算出最少再过多少天后三个峰值同时出现。

问题分析
首先我们要知道,任意两个峰值之间一定相距整数倍的周期。假设一年的第N天达到峰值,则下次达到峰值的时间为N+Tk(T是周期,k是任意正整数)。所以,三个峰值同时出现的那一天(S)应满足

  S = N1 + T1*k1 = N2 + T2*k2 = N3 + T3*k3

N1,N2,N3分别为为体力,情感,智力出现峰值的日期, T1,T2,T3分别为体力,情感,智力周期。 我们需要求出k1,k2,k3三个非负整数使上面的等式成立。

本题难点不在编程,而是分析题目并转化为数学公式
要引入本题解法,先来看一个故事 “韩信点兵”:
传说西汉大将韩信,由于比较年轻,开始他的部下对他不很佩服。有一次阅兵时,韩信要求士兵分三路纵队,结果末尾多2人,改成五路纵队,结果末尾多3人,再改成七路纵队,结果又余下2人,后来下级军官向他报告共有士兵2395人,韩信立即笑笑说不对(因2395除以3余数是1,不是2),由于已经知道士兵总人数在2300~2400之间,所以韩信根据23,128,233,——,每相邻两数的间隔是105(3、5、7的最小公倍数),便立即说出实际人数应是2333人(因2333=128+20χ105+105,它除以3余2,除以5余3,除以7余2)。这样使下级军官十分敬佩,这就是韩信点兵的故事。

韩信点兵问题简化:已知 n%3=2, n%5=3, n%7=2, 求n。

再看我们这道题,读入p,e,i,d 4个整数
已知(n+d)%23=p; (n+d)%28=e; (n+d)%33=i ,求n 。

两道题是一样的。但是韩信当时计算出结果的?
韩信用的就是“中国剩余定理”,《孙子算经》中早有计算方法,大家可以查阅相关资料。
“韩信点兵”问题计算如下:
因为n%3=2, n%5=3, n%7=2 且 3,5,7互质 (互质可以直接得到这三个数的最小公倍数)
令x= n%3=2 , y= n%5=3 ,z= n%7=2
使5×7×a被3除余1,有35×2=70,即a=2;
使3×7×b被5除余1,用21×1=21,即b=1;
使3×5×c被7除余1,用15×1=15,即c=1。
那么n =(70×x+21×y+15×z)%lcm(3,5,7) = 23 这是n的最小解
而韩信已知士兵人数在2300~2400之间,所以只需要n+i×lcm(3,5,7)就得到了2333,此时i=22

同样,这道题的解法就是:
已知(n+d)%23=p; (n+d)%28=e; (n+d)%33=i
使33×28×a被23除余1,用33×28×8=5544;
使23×33×b被28除余1,用23×33×19=14421;
使23×28×c被33除余1,用23×28×2=1288。
因此有(5544×p+14421×e+1288×i)% lcm(23,28,33) =n+d
又23、28、33互质,即lcm(23,28,33)= 21252;
所以有n=(5544×p+14421×e+1288×i-d)%21252
本题所求的是最小整数解,避免n为负,因此最后结果为n= [n+21252]% 21252
那么最终求解n的表达式就是:
n=(5544*p+14421*e+1288*i-d+21252)%21252;
当问题被转化为一条数学式子时,你会发现它无比简单。。。。直接输出结果了。

#include <iostream>
#include <algorithm>
using namespace std;

int main()
{
    int p, e, i, d;
    int t = 1;
    int ans;
    while(cin >> p >> e >> i >> d){
        if(p == -1 && e == -1 && i == -1 && d == -1)
            break;
        ans = (5544*p+14421*e+1288*i-d+21252)%21252;
        if(ans == 0) ans += 21252;
        cout << "Case " << t++ << ": the next triple peak occurs in " << ans << " days." << endl;
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值