题解 P1292 【倒酒】

欧几里得与扩展欧几里得

博客食用 , 效果更佳: 欧几里得 与 扩展欧几里得 - 核融合炉心 - 洛谷博客

以下,是本题需要的所有前置知识(大概吧wwww),

如果您已经学会了所有的前置知识,
请忽略 \(Baka\) 阿空的废话
直接看关于本题的内容


目录:

  • 1.欧几里得定理
    • 证明
    • 应用
  • 2.裴蜀定理

  • 3.扩展欧几里得
    • 证明
    • 求解线性不定方程
  • 4.关于本题


1.欧几里得定理:

即 : \(\large gcd(a,b) = gcd(b,a\mod b)\)

\(b=0\) 时,\(gcd(a,b)= \mid a\mid\) ;

  • 感谢cyh学长给的证明 :

    设 : \(a,b\) 的最大公约数为 \(c\)

    则 : \(a=nc\ ,\ b=mc\) , \((n,m \in Z)\) , \(a=k\times b + r\)

    则 : \(r=a\%b=a-k b=nc-kmc=(n-km)c\)

    若要使 \((a,b) = (b,a\%b)\) ,

    则需要证 : \(b , r\) 的最大公约数也为 \(c\) ,

    即 : \(b=mc , r=(n-km)c\) 中 , \(m,(n-km)\) 互质 。

    • 子证明:

      用反证法 , 设存在 \(d\)\(m,(n-km)\) 的最大公约数,且 \(d > 1\)

      设 : \(n-km=qd\) , \(m=pd\)

      则 : \(b = mc = pdc\\) ,

      \(\ a=kb+r=kpdc + qdc=dc(kp+q)\)

      则 : \(a\) 还存在一个因数 \(dc > c\)

      此结论与\(a,b\) 的最大公约数为 \(c\) 相矛盾

      故 : 不存在 \(d>1\) 作为 \(m,(n-km)\) 的最大公约数

      则 : \(m,(n-km)\)互质 ,子证明成立。

    由子证明 ,得: \(b=mc , r=(n-km)c\) 中 , \(m,(n-km)\) 互质 。

    则: \(b\)\(r\) 的最大公因数仍为 \(c\)

    证毕 。


  • 应用:

    有了以上的知识,我们便可以写出一个求\(a,b\)\(gcd(a,b)\) 的函数了

    由 欧几里得定理可得 , 要通过递归来求得\(gcd(a,b)\)的值

    递归边界即: 当 \(b=0\) 时,\(gcd(a,b)=a\) ;

    简单的代码实现:

int gcd(int a,int b)
{
    if(b) 
      return gcd(b,a%b);
    else 
      return a;
}

也可以写成一行:

int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}

2. 裴蜀定理:

\(a,b\) 为不全为 \(0\) 的整数 , 则存在整数 \(x,y\) ,使得 \(\large ax + by = gcd(a,b)\)

特别地 , \(gcd(a,b)=1\) \(\Leftrightarrow\)
存在 \(x,y \in Z\) , 使: \(ax+by = 1\) ;

那么该如何证明 定理的正确性 呢 ? 请继续往下看:


3. 欧几里得扩展:

对于不完全为 \(0\) 的两个数 \(a,b\) ,必然存在 无限个\(x,y\) ,使方程
\(\large ax + by = gcd(a,b)\)成立

  • 证明:

    \(a>b\)

    1. 当$b=0 $时 , \(gcd(a,b)=a\) ,此时有唯一的解 : \(x=1,y=0\);

    2. \(a\times b\not= 0\) 时 ,

    根据欧几里得定理 : $ gcd(a,b) = gcd(b,a% b)$ , 可知:

    \(\underline{ax + by }= gcd(a,b) = gcd(b,a\% b)=\underline{b {x1} + (a\% b) y1}\)

    ( 设\(x1,y1\)是满足如上情况的一组解 )

    而:\(a\% b = a-\lfloor \frac{a}{b}\rfloor \times b\)

    则: 原等式可转化为:

    ①. \(ax + by = bx1 + (a-\lfloor \frac{a}{b}\rfloor \times b)y1\)

    ②. \(ax + by = bx1 + ay1 -\lfloor \frac{a}{b}\rfloor by 1\)

    ③. \(ax + by = ay1 +b(x1-\lfloor \frac{a}{b}\rfloor y 1)\)

    由③,可得:

    \(x = y1\)

    $y=x1- \lfloor\frac{a}{b}\rfloor y1 $

    这样 , 就可以得到 \(ax+by = c\) 的一组解

    方程其他整数解 \(xi,yi\) 满足 :

    $xi = x + \frac{b}{gcd(a,b)} \times t $

    $yi = y + \frac{a}{gcd(a,b)} \times t $

    其中 , \(t \in Z\)

  • 代码实现

    要想写一个求解 \(ax + by = gcd(a,b)\) 的程序

    通过以上的证明 , 显然 , 可以通过递归实现

    递归边界即 : 当$b=0 $时 , \(gcd(a,b)=a\) ,此时有唯一的解 : \(x=1,y=0\);

    代码:
    cpp int exgcd(int a,int b,int d,int &x,int &y) { if(!b) { x=1 , y=0; return a; } d=exgcd(b,a % b,x,y); int tmp=x; x=y , y=tmp-a/b*y; }
  • 求解不定方程:

    对于不定方程 : $ ax + by = c $ :

    根据 扩展欧几里得 , 可知 :

    对于方程\(ax + by = gcd(a,b)\) , 有无数组解 \(x,y\).

    则 :

    1.若$ c% gcd(a,b) \not= 0 $ , 则原方程无解。

    2.若\(c \% gcd(a,b) = 0\) :

    \(d = gcd(a,b)\) , 则原方程可转化为 :

    \(a\times (x\times \frac{d}{c}) + b\times (y\times \frac{d}{c}) =d (= c\times \frac{d}{c})\)

    换元,使 \(x1=(x\times \frac{d}{c})\) , \(y1=(y\times \frac{d}{c})\);

    解方程: \(ax1 + by1 = d\) , 有无数组解 \(x1,y1\),

    求解出 \(x1,y1\) 后 , 可得:

    \(x=x1 \times \frac{c}{d}\) , \(y=y1 \times \frac{c}{d}\)

    即可得到原方程 \(ax + by = c\) 的解


4.关于本题:

(啰嗦这么多终于开始说正事了)

  • 题目中的三种操作:

    ①将酒桶中的酒倒入容积为\(b\ ml\)的酒杯中;
    ②将容积为\(a\ ml\)的酒杯中的酒倒入酒桶;
    ③将容积为\(b\ ml\)的酒杯中的酒倒入容积为\(a\ ml\)的酒杯中。

  • 题意分析:

    最后剩余的最小酒量\(ans\)
    每次添加 \(b\ ml\)的酒 , 每次倒出 \(a\ ml\) 的酒

    则有:
    $ans = -ax_1 + by_1 (x_1,y_1 \in Z) $

    欲使此不定方程有解
    \(ans = k\times \gcd(a,b) \ (k\in Z)\)
    而要求 \(ans\)最小非负整数解
    \(ans = gcd(a,b)\)

  • 算法实现:

    用扩展欧几里得
    解不定方程 \(\gcd(a,b) = ax_1 + by_1\)
    得到了 一组 \(x_1\)\(y_1\) 的解

    然后使 \(x_1\times(-1)\ ,\ a\times(-1)\);
    原方程转化为:
    \(\underline{ax_1 + by_1} = (-a)\times(-x_1) + by_1 = \underline{-ax + by}\)
    得到:
    \(x= -x_1\)
    \(y=y_1\)

    然后通过
    cpp while(x<0 || y<0) x+= (x<0 )?b/gcd:0, y-= (x>=0)?a/gcd:0;

    \(x,y\) 都转化为最小的非负整数解
    即得答案


\(AC\) 代码
#include<cstdio>
#include<ctype.h>
//====================================================
int a,b,x,y,gcd;
//====================================================
inline int read()
{
    int fl=1,w=0;char ch=getchar();
    while(!isdigit(ch) && ch!='-') ch=getchar();
    if(ch=='-') fl=-1;
    while(isdigit(ch)){w=w*10+ch-'0',ch=getchar();}
    return fl*w;
}
void exgcd(int a,int b)//扩展欧几里得求线性不定方程的解 
{
    if(b)
    {
        exgcd(b,a%b);
        int k=x;
        x=y , y=k-a/b*y;
    }
    else 
      x=1,y=0,gcd=a;
    return ;
}
//====================================================
signed main()
{
    a=read(),b=read();
    exgcd(a,b);
    x*=-1,a*=-1;//进行转化 
    while(x<0 || y<0)//获得最小的非负整数解 
      x+= (x<0 )?b/gcd:0,
      y-= (x>=0)?a/gcd:0;
    
    printf("%d\n%d %d",gcd,x,y);
}

猜你喜欢

转载自www.cnblogs.com/luckyblock/p/11456326.html