[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 }

code



最后再复习一下扩展欧几里得算法:

对于 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也一样

 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 }

解方程

时间: 2024-10-08 23:17:32

[NOI2002] 荒岛野人 扩展欧几里得算法的相关文章

JZYZOJ1372 [noi2002]荒岛野人 扩展欧几里得

http://172.20.6.3/Problem_Show.asp?id=1372 想法其实很好想,但是我扩展欧几里得还是用得不熟练,几乎是硬套模板,大概因为今天一个下午状态都不大好.扩展欧几里得算法计算的是 : ab互质时ax+by=1或ab不互质时ax+by=gcd(a,b)(废话)的一个整数解,可以据此推导一个方程是否有解.然后我理解这个基本概念理解了一个下午,非常智障了.这道题也是模板,两两对比即可. 代码 1 #include<iostream> 2 #include<cst

扩展欧几里得算法的模板实现

我居然现在还记不住扩欧的板子,我太弱啦! 扩展欧几里得算法解决的是这样的问题: 给定一个不定方程组ax+by=gcd(a,b),求他的一组整数解 先给出实现代码 void exgcd(int a,int b,int &x,int &y) { if(!b) { x=1,y=0;//gcd(a,0)显然等于1*a-0*0=a return a; } int ans=exgcd(b,a%b,x,y); int tem=x; x=y; y-=tem-(a/b)*y; return ans;} 但实

欧几里得算法与扩展欧几里得算法_C++

先感谢参考文献:http://www.cnblogs.com/frog112111/archive/2012/08/19/2646012.html 注:以下讨论的数均为整数 一.欧几里得算法(重点是证明,对后续知识有用) 欧几里得算法,也叫辗转相除,简称 gcd,用于计算两个整数的最大公约数 定义 gcd(a,b) 为整数 a 与 b 的最大公约数 引理:gcd(a,b)=gcd(b,a%b) 证明: 设 r=a%b , c=gcd(a,b) 则 a=xc , b=yc , 其中x , y互质

POJ - 1061 青蛙的约会 (扩展欧几里得算法)

Description 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止.可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置.不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的.但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的.为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面. 我们把这

扩展欧几里得算法(extgcd)

相信大家对欧几里得算法,即辗转相除法不陌生吧. 代码如下: int gcd(int a, int b){ return !b ? gcd(b, a % b) : a; } 而扩展欧几里得算法,顾名思义就是对欧几里得算法的扩展. 切入正题: 首先我们来看一个问题: 求整数x, y使得ax + by = 1, 如果gcd(a, b) != 1, 我们很容易发现原方程是无解的.则方程ax + by = 1有正整数对解(x, y)的必要条件是gcd(a, b) = 1,即a, b 互质. 此时正整数对解

欧几里得算法以及扩展欧几里得算法(过河noip2005提高组第二题)

欧几里得算法:也被称作辗转相除法 gcd(a,b)=gcd(b,a%b); 终止条件a=gcd b=0; (gcd为a,b的最大公约数) 扩展欧几里得算法: a 和 b 的最大公约数是 gcd ,一定能够找到这样的 x 和 y ,使得: a*x + b*y = gcd 成立 我们只需要找到特殊解x0,y0; 则通解为 x = x0 + (b/gcd)*t    y = y0 – (a/gcd)*t 那如何求出下一组解呢 仿照欧几里得算法a=b,b=a%b代入. a%b = a - (a/b)*b

扩展欧几里得算法学习记

话说以前我刷noip题的时候就想学这个东西了,结果却一直拖到了现在…… 到了高二才会这种东西的我实在是个蒟蒻啊! 将扩展欧几里得算法之前,先讲讲欧几里得算法是什么:gcd(a,b)=gcd(b,a%b).很显然是不?但我们还是要给出证明(设r=a%b): 设x是a,b的一个公约数,由于存在k使得a=k*b+r,又由于a|x,b|x,则有r|x,所以x是b,r的公约数 设x是b,r的一个公约数,因为存在k使得a=k*b+r,且b|x,r|x,那么a|x,所以x是a,b的公约数 综上所述,a和b的所

HDU - 1576 A/B(扩展欧几里得算法)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1576 题意:要求(A/B)%9973,但由于A很大,我们只给出n(n=A%9973)(我们给定的A必能被B整除,且gcd(B,9973) = 1). 普通版欧几里得算法(辗转相除): 1 typedef long long LL; 2 LL gcd(LL a,LL b){ 3 return (b==0) ? a : gcd(b,a%b); 4 } 扩展欧几里得算法(理论):对于不完全为0的非负整数,

POJ 1061 青蛙绕地球约会-数论-(解一元一次同余方程+扩展欧几里得算法)

题意:两只青蛙同向跳,起点是x,y,每次分别跳m,n米,地球周长是L,求最少跳几次相遇. 分析: 把式子写好就发现是一个一元一次同余方程.用扩展欧几里得算法来求.这题很基本得会. 代码: #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<string> #include<queue> #define INF 100000