初见poj1006用的暴力破解写的代码,写完以后发现很搓。上网一查原来古人早就对此类问题有了解答规则。
一、中国剩余定理
一元线性同余方程组问题最早可见于中国南北朝时期(公元5世纪)的数学著作《孙子算经》卷下第二十六题,叫做“物不知数”问题,原文如下:
有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?
即,一个整数除以三余二,除以五余三,除以七余二,求这个整数。《孙子算经》中首次提到了同余方程组问题,以及以上具体问题的解法,因此在中文数学文献中也会将中国剩余定理称为孙子定理。
宋朝数学家秦九韶于1247年《数书九章》卷一、二《大衍类》对“物不知数”问题做出了完整系统的解答。明朝数学家程大位将解法编成易于上口的《孙子歌诀》:
三人同行七十希,五树梅花廿一支,七子团圆正半月,除百零五使得知
这个歌诀给出了模数为3、5、7时候的同余方程的秦九韶解法。意思是:将除以3得到的余数乘以70,将除以5得到的余数乘以21,将除以7得到的余数乘以15,全部加起来后除以105,得到的余数就是答案。比如说在以上的物不知数问题里面,使用以上的方法计算就得到
因此按歌诀求出的结果就是23.
大致过程是:
n1*5*7%3=1,则n1最小为2;2*5*7=70.
n2*3*7%5=1,则n2最小为1;1*3*7=21.
n3*5*3%7=1,则n3最小为1;1*3*5=15.
n%3=2;
n%5=3;
n%7=2;
所以n=(70*2+21*3+15*2)%(3*5*7)=233%105=23.
二、求解
有了上边的算法,求解poj1006就很简单啦。由题意我们很容易得到:
(n+d)%23=p;
(n+d)%28=e;
(n+d)%33=i;
并且,若a%b=c, k为正整数,则k*a%b=(k*c)%b
33*28%23=4,则6*33*28%23=4*6%23=1; 6*33*28=5544
23*33%28=3,则19*23*33%28=19*3%28=1; 19*23*33=14421
28*23%33=17,则2*28*23%33=2*17%33=1; 2*28*23=1288
23、28、33互质,最小公倍数23*28*33=21252
(5544*p+14421*e+1288*i)%21252=n+d
代码如下:
#include "stdafx.h"
#include<iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
int p,e,i,d;
cin>>p>>e>>i>>d;
int num=0;
int arr[10000][4];//用二维数组存储输入
while(!(p==-1&&e==-1&&i==-1&&d==-1)){
arr[num][0]=p;
arr[num][1]=e;
arr[num][2]=i;
arr[num][3]=d;
num++;//num记录输入数据的组数
cin>>p>>e>>i>>d;
}
for(int i=0;i<num;i++){
int j=(5544*arr[i][0]+14421*arr[i][1]+1288*arr[i][2])%21252-arr[i][3];
if(j<=0)//防止出现输出0或者负数的情况
j+=21252;
cout<<"Case "<<i+1<<": the next triple peak occurs in "<<j<<" days."<<endl;
}
return 0;
}