[NOI2002] 荒岛野人 扩展欧几里得算法
【问题描述】
克里特岛以野人群居而著称。岛上有排列成环行的M个山洞。这些山洞顺时针编号为1,2,…,M。岛上住着N个野人,一开始依次住在山洞 C1,C2,…,CN中,以后每年,第i个野人会沿顺时针向前走Pi个洞住下来。每个野人i有一个寿命值Li,即生存的年数。下面四幅图描述了一个有6个 山洞,住有三个野人的岛上前四年的情况。三个野人初始的洞穴编号依次为1,2,3;每年要走过的洞穴数依次为3,7,2;寿命值依次为4,3,1。
奇怪的是,虽然野人有很多,但没有任何两个野人在有生之年处在同一个山洞中,使得小岛一直保持和平与宁静,这让科学家们很是惊奇。他们想知道,至少有多少个山洞,才能维持岛上的和平呢?
【输入文件】
输入文件的第1行为一个整数N(1<=N<=15),即野人的数目。第2行到第N+1每行为三个整数Ci, Pi, Li (1<=Ci,Pi<=100, 0<=Li<=106 ),表示每个野人所住的初始洞穴编号,每年走过的洞穴数及寿命值。
【输出文件】
输出文件仅包含一个数M,即最少可能的山洞数。输入数据保证有解,且M不大于10^6。
solution
(其实这个题根据 M不大于10^6 知道要枚举M)
我一开始思路是:
设当前总共的洞穴数是m个
对于每两个野人,如果他们会相遇,那么会有
(C[i]+k*P[i])≡(C[j]+k*P[j]) (%m)
即 m*t1=C[i]+k*P[i]...① m*t2=C[j]+k*P[j]...②
①+②得 m*(t1+t2)-(P[i]+P[j])*k=C[i]+C[j]
可是这样没法解方程,就放弃了
看题解是:
对于野人i (C[i]+k*P[i])%m=S1→C[i]+k*P[i]-k1*m=S1
对于野人j (C[j]+k*P[j])%m=S2→C[j]+k*P[j]-k2*m=S2
相遇的条件是 S1==S2
所以两式相减 (P[i]-P[j])*k+x*m=C[j]-C[i] (此时野人相遇S1==S2)
最后如果方程 无解 OR k>L[i]||k>L[j] 即为不相遇
1 #include<cstring> 2 #include<cstdio> 3 #include<iostream> 4 #include<cstdlib> 5 #define ll long long 6 #define dd double 7 #define mem(a,b) memset(a,b,sizeof(a)) 8 using namespace std; 9 inline int maxn(int a,int b){return a>b?a:b;} 10 int gcd(int x,int y){return y==0?x:gcd(y,x%y);} 11 12 int n,be; 13 int C[21],p[21],L[21]; 14 15 void egcd(int a,int b,int &x,int &y) 16 { 17 if(b==0) 18 { 19 x=1; 20 y=0; 21 return ; 22 } 23 egcd(b,a%b,x,y); 24 int temp=x; 25 x=y; 26 y=temp-a/b*y; 27 } 28 29 int check(int now) 30 { 31 int x,y,d,a,b,c; 32 for(int i=1;i<=n;++i) 33 for(int j=i+1;j<=n;++j) 34 { 35 a=p[i]-p[j]; 36 b=now; 37 c=C[j]-C[i]; 38 d=gcd(a,b); 39 40 if(c%d!=0) 41 continue; 42 a/=d; 43 b/=d; 44 c/=d; 45 egcd(a,b,x,y); 46 b=abs(b); 47 x=((x*c)%b+b)%b; 48 if(x==0) 49 x+=b; 50 if(x<=min(L[i],L[j])) 51 return 0; 52 } 53 return 1; 54 } 55 56 int main(){ 57 58 //freopen("savage.in","r",stdin); 59 //freopen("savage.out","w",stdout); 60 61 //freopen("1.txt","r",stdin); 62 63 scanf("%d",&n); 64 for(int i=1;i<=n;++i) 65 { 66 scanf("%d%d%d",&C[i],&p[i],&L[i]); 67 be=maxn(be,C[i]); 68 } 69 for(int i=be;i<=1000000;++i) 70 if(check(i)) 71 { 72 cout<<i; 73 break; 74 } 75 //while(1); 76 return 0; 77 }
最后再复习一下扩展欧几里得算法:
对于 ax+by=c
1.如果 c%gcd!=0,无解
2.求出gcd=gcd(a,b) a/=gcd b/=gcd c/=gcd 先约分
3.egcd 解方程
4.x*c y*c 是此方程一组解
5.如果要 求x,那么b=abs(b),x=x0+b*t(b已经约分,不用再/gcd)
求y也一样
6.
int a,b;
a>0,b<0
此时a%b,应该是b先=abs(b),a再%b.....
1 int gcd(int x,int y) 2 { 3 return y==0?x:gcd(y,x%y); 4 } 5 6 void egcd(int a,int b,int &x,int &y) 7 { 8 if(b==0) 9 { 10 x=1; 11 y=0 12 return ; 13 } 14 int temp=x; 15 x=y; 16 y=temp-a/b*y; 17 } 18 19 int cal(int a,int b,int c) 20 { 21 int x,y,d; 22 d=gcd(a,b); 23 if(c%d) 24 return -1; 25 a/=d;b/=d;c/=d; 26 egcd(a,b,x,y); 27 x=((c*x)%b+b)%b; 28 if(x==0)x+=b; 29 return x; 30 }