poj 1006 Biorhythms

1.5 中国剩余定理

出自Bupt_wiki

 
跳转到: 导航, 搜索
这篇文章写的很清晰,所有就COPY过来了,与大家分享;

●知识精讲

这个问题源自于我国数学古书《孙子算经》中的一道问题:“今有物,不知其数,三三数之,剩二;五五数之,剩三;七七数之,剩二。问物几何?”意思是一个整数除以三余二,除以五余三,除以七余二,求这个整数(满足条件且最小)。

做法是:

 n%3=2,n%5=3,n%7=2且3,5,7互质
 找到使(5×7)的倍数模3得1的数,答案是70
 找到使(3×7)的倍数模5得1的数,答案是21
 找到使(3×5)的倍数模7得1的数,答案是15
 那么(70×2+21×3+15×2) % (3×5×7) = 23,23就是最终答案了

理解如下:

70×2 = 140,当中的2是指n%3 = 2的2,因为70%3=1,乘2后使其能满足%3=2的条件,同理,
21×3使其能满足%5=3
15×2使其能满足%7=2
又70能整除5和7,21能整除3和7,15能整除3和5
因此70×2+21×3+15×2 = 233能满足%3=2,%5=3,%7=2的条件
将233对3×5×7=105取模,是为了取得最小的情况,因为105是能够同时整除3,5,7的数,如果减去,对该数满足%3=2,%5=3,%7=2没有影响。因此对105取模,得到的结果便是最小并满足条件的数

拓展开来问题就是有同余方程组 a = ai (mod ni), 求未知数a。方法是:

定义 n=n1*n2...nk, 其中因子两两互质
mi = n1*n2*...nk / ni;   
ci = mi(mf  mod ni);   
其中 mi*mf  mod ni = 1;
则 a = (a1*c1+a2*c2+...+ak*ck) (mod n) 

中国剩余定理关键是mf的求法,如果理解了扩展欧几里得 ax+by=d, 就可以想到:

          mi*mf  mod ni = 1 => mi*mf+ni*y=1;

这里的中国剩余定理必须要求除数是互质的,但是有些题目的同余方程式除数并不互质,那么将不能使用传统的中国剩余定理

●例题分析

问题描述:POJ 1006

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

问题分析:

这一题的做法可以类比算经中问题的方法

找到使(23×28)的公倍数模33得1的数,答案是1288

找到使(23×33)的公倍数模28得1的数,答案是14421|

找到使(28×33)的公倍数模23得1的数,答案是5544

(5544×p+14421×e+1288×i) % (23×28×33) = ans + d

若使用中国剩余定理的一般情况下的处理,则可按照一般情况的方法写出程序,参考核心程序为:

int mod[3],res[3];//除数,余数
void exGCD(int a,int b,int &x,int &y)//扩展欧几里得 
{
   if (b==0){
   	  x = 1;y = 0;return;
   }
   exGCD(b,a%b,x,y);
   int temp = x;
   x = y;
   y = temp-a/b*y;
}  
int china_mod()//中国剩余定理 
{
    int mul=1,ans=0,mf,y,mi;
    for (int i=0; i<3; i++)
       mul *= mod[i];
    for (int i=0; i<3; i++){
    	mi = mul/mod[i];
    	exGCD(mi,mod[i],mf,y);
    	ans += (mi*mf*res[i])%mul;
    }
    return (ans+mul)%mul;
}
View Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<cstring>
#include<string>
#include<vector>
#define LL long long
using namespace std;
LL mod[3] = {23,28,33};
LL Ex_Gcd( LL a , LL b , LL &x ,LL &y )
{
   if( b == 0 )
   {
       x = 1;
       y = 0;
       return a;        
   }
   LL mod = Ex_Gcd( b , a % b , x , y );
   LL temp = x;
   x = y;
   y = temp - (a/b)*y;
   return mod;
}
LL Solve(  LL mul ,LL re[])
{
    LL ans = 0,x,y;
       for( int i = 0; i < 3 ; i ++ )
       {
        LL mi = mul/mod[i];
        Ex_Gcd( mi , mod[i] , x ,y  );
        ans +=(x*re[i]*mi )%mul;
    }
    return  (ans+mul)%mul;
}
int main(  )
{
    LL mul = 1;
    for( int i = 0 ; i < 3 ; i ++ )
         mul *= mod[i];
    LL re[3],d;
    int Case = 1;
    while( 1 )
    {
        int cnt = 0;
        for( int i = 0 ; i < 3 ; i ++ )
        {
          scanf( "%I64d",&re[i] );
          if( re[i] == -1 ) cnt++;
        }
        scanf( "%I64d",&d );
        if( d==-1&&cnt == 3 ) break;
        LL ans = Solve( mul ,re) -d;
        while( ans <= 0 ) ans += mul;
        printf( "Case %d: the next triple peak occurs in %I64d days.\n",Case++,ans  );
    }
    //system( "pause" );
    return 0;
}

 

转载于:https://www.cnblogs.com/bo-tao/archive/2012/07/20/2601352.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值