一个正整数K,给出K Mod 一些质数的结果,求符合条件的最小的K。例如,K % 2 = 1, K % 3 = 2, K % 5 = 3。符合条件的最小的K = 23。
Input
第1行:1个数N表示后面输入的质数及模的数量。(2 <= N <= 10) 第2 - N + 1行,每行2个数P和M,中间用空格分隔,P是质数,M是K % P的结果。(2 <= P <= 100, 0 <= K < P)
Output
输出符合条件的最小的K。数据中所有K均小于10^9。
Input示例
3 2 1 3 2 5 3
Output示例
23
中国剩余定理也叫孙子定理:
《孙子算经》里面的"物不知数"说的是这样的一个题目:一堆东西不知道具体数目,3个一数剩2个,5个一数剩3个,7个一数剩2个,问一共有多少个。
书里面给了计算过程及答案:70*2 + 21*3 + 15*2 -105*2 = 23。
它的计算思路如下:
70是能被5或7整除的数字,但是除以3正好余1。
21是能被3或7整除的数字,但是除以5正好余1。
15是能被3或5整除的数字,但是除以7正好余1。
所以若有数N = 70 * N1 + 21 * N2 + 15 * N3(其中N,N1,N2,N3为正整数),则整数N是符合题目要求的结果(mod3为N1,mod5为N2, mod7为N3)。
我们把N1赋值2,N2赋值3,N3赋值2。
则: N = 70*2 + 21*3 + 15*2 = 233。
但是3,5,7的最小公倍数为105。
所以N + 105*M均为正解。
因此为了求得最小正整数解,我们需要用(N mod 105)也就是23了。
所以就有代码:#include <stdio.h>
#include <iostream>
using namespace std;
int main(int argc, const char * argv[])
{
int N, i = 0, j = 0;
int P[11], M[11];
long long sum = 0, K, acc = 1;
scanf("%d", &N);
for (; i < N; i++)
{
scanf("%d %d", P + i, M + i);
acc *= P[i];///acc对应105
}
for (i = 0; i < N; i++)
{
for (j = 1; j < 100000; j++)
{
if (acc / P[i] * j % P[i] == 1)///对应黑体字部分,模上对应的模数正好为1
{
sum += acc / P[i] * j * M[i];///N=70*N1+21*N2+15*N3
break;
}
}
}
K = sum % acc;
printf("%lld\n", K);
return 0;
}
相信代码不难理解了,接下来是利用扩展欧几里得算法求,求解和证明过程有点难以理解,保留大神博客链接:
大神解释定理:http://blog.csdn.net/acdreamers/article/details/8050018
大神证明定理:http://blog.csdn.net/hard_man/article/details/7732795
代码如下:
#include <iostream>
#include <stdio.h>
#include <string.h>
#define LL long long
using namespace std;
int a[12];//余下的数
int m[12];//模数
///参数可为负数的扩展欧几里德定理
LL int exgcd(LL int a,LL int b,LL int &x,LL int &y){
if(b==0){
x=1;
y=0;
return a;
}
LL int x1,y1;
LL int d=exgcd(b,a%b,x1,y1);
if(a*b<0){
x=-y1;
y=a/b*y1-x1;
}else{
x=y1;
y=x1-a/b*y1;
}
return d;
}
LL int calLSYDL(int a[],int m[],int k){///中国剩余定理
LL int N[k];
LL int M=1;
LL int result=0;
for(int i=0;i<k;i++){
M*=m[i];
}
for(int i=0;i<k;i++){
LL int x,y;
exgcd(M/m[i],-m[i],x,y);
N[i]=M/m[i]*x;//1
N[i]=m[i]*y+1;//2【注】 1和2两者值相同
result+=N[i]*a[i];
}
return (result%M+M)%M;
}
int main()
{
int t;
scanf("%d",&t);
for(int i=0;i<t;i++){
scanf("%d %d",m+i,a+i);
}
LL int result=calLSYDL(a,m,t);
cout<<result<<endl;
return 0;
}
计算过程按照大神博客那样分开了,方便日后理解,没有一步到位。Pass:总算把中国剩余定理解决,也更加加深了对扩展欧几里得算法的理解。