51nod--中国剩余定理

                                                                       1079 中国剩余定理
 收藏
 关注
一个正整数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:总算把中国剩余定理解决,也更加加深了对扩展欧几里得算法的理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值