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;

}

 

你可能感兴趣的:(poj)