题目地址:POJ 2891
题意:给你k组同余关系,每组包含一个ai和ri,让你找出一个最小的数m,满足m%a1=r1,m%a2=r2.......m%ak=rk。
思路:纵观上述公式,很熟悉,其实就是求两两公式之间的最小值,例如K=3,那么先求第一组和第二组的最小,然后合并第一组和第二组,然后用合并之后的再和第三组找最小,最后的结果就是最终的结果。也就是这个题分两部分来完成。
1.找出两组最小。对于m%a1=r1和m%a2=r2可以得出两个公式m=a1*x+r1,m=a2*y+r2(x,y相当于m/ai的次数),然后联立得到a1*x+a2*y=r2-r1.然后就套用扩展欧几里德得出最小的x。
2.合并。到底如何合并,新式子是这样的:m1mod lcm(a1,a2) ==m,即m1=lcm(a1+a2)*x+m(m1位新要求的值,m为求出来的当前最小的值),然后把x*lcm(a1,a2)赋值给a1,把m赋值给r1。
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <set>
#include <queue>
#include <stack>
#include <map>
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const double pi= acos(-1.0);
const double esp=1e-6;
LL gcd(LL a,LL b)
{
while(b!=0){
LL r=b;
b=a%b;
a=r;
}
return a;
}
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(b==0){
x=1;y=0;
return a;
}
LL r=exgcd(b,a%b,x,y);
LL t=x;
x=y;
y=t-(a/b)*y;
return r;
}
int main()
{
int n;
LL a1,r1,a2,r2;
LL a,b,d,r;
LL x1,y1,t;
while(~scanf("%d",&n)){
scanf("%lld %lld",&a1,&r1);
if(n==1)
printf("%lld\n",a1+r1);
else{
int flag=0;
for(int i=1;i<n;i++){
scanf("%lld %lld",&a2,&r2);
if(!flag){
a=a1;
b=a2;
d=r2-r1;
r=gcd(a,b);
if(d%r){
flag=1;
continue;
}
a/=r;
b/=r;
d/=r;
exgcd(a,b,x1,y1);
x1=x1*d;
y1=y1*d;
x1=(x1%b+b)%b;//此处用的是b的原因是对于公式a1*x1+a2*y1=m前面的a1*x1可能是负的,所以a1*(x1+a2*n)+a2*(y1-a1*n)=m。
r1=a1*x1+r1;
a1=a1/r*a2;
}
}
if(flag)
puts("-1");
else
printf("%lld\n",r1);
}
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
POJ 2891-Strange Way to Express Integers(扩展欧几里德)
原文地址:http://blog.csdn.net/u013486414/article/details/47090535