Jams倒酒

【题目描述】

Jams有两种酒杯,容积分别为AmL、BmL,并且他只能在两种酒杯和一个酒桶之间反复倾倒,从而得到新的体积的酒。

现规定:

(1)A >= B;

(2)酒桶容积无限;

(3)只能包含三种可能的倒酒操作:

①将酒桶中的酒倒入容积为BmL的酒杯中;

②将容积为AmL的酒杯中的酒倒入酒桶;

③将容积为BmL的酒杯中的酒倒入容积为AmL的酒杯中;

(4)每次倒酒必须把酒杯倒满或者把被倾倒的酒杯倒空;

Jams希望倾倒若干次,使容积为AmL的酒杯中剩下的酒的体积尽可能小。

【输入描述】

输入两个整数A、B。

【输出描述】

第一行输出一个整数,表示能够得到的最小体积的酒;

第二行输出两个整数PA、PB,分别表示用容积为AmL的酒杯倒酒的次数以及将酒倒入容积为BmL的酒杯的次数。

如果有多组PA、PB满足要求,优先输出PA最小的那一组,如果当PA最小时,有多个PB满足要求,优先输出最小的PB。

【样例输入】

5 3

【样例输出】

1

1 2

【数据范围及提示】

样例的倾倒方案为:

(1)桶 --> B;

(2)B --> A;

(3)桶 --> B;

(4)B --> A;

(5)A --> 桶;

(6)B --> A;

对于20%的数据,PA+PB <= 5;

对于60%的数据,PA <= 108

对于100%的数据,0 < B <= A <= 109

源代码:

#include<cstdio>
#define LL long long
LL A,B,X,Y,Min;
LL GCD(LL t1,LL t2) //辗转相除法。
{
    return t2?GCD(t2,t1%t2):t1;
}
void ExGCD(LL t1,LL t2) //拓展欧几里得算法。
{
    if (!t2) //此时,形成了GCD(A,0)=A,原式变为Ax+By=A,则x=1,y=0。
    {
        X=1;
        Y=0;
        return;
    }
    else
    {
        ExGCD(t2,t1%t2);
        LL t=X;
        X=Y;
        Y=t-t1/t2*Y;
    }
}
int main()
{
    scanf("%I64d%I64d",&A,&B);
    Min=GCD(A,B);
    A/=Min;
    B/=Min; //也算是优化。
    ExGCD(A,B);
    while (Y<0)
      Y+=A; //合法。
    X=(1-Y*B)/A; //根据方程式暴力强求。
    printf("%I64d\n%I64d %I64d",Min,-X,Y); //想一想,这样即为最小值。
    return 0;
}

/*
    很不错的一道NOIP阶段数论题。
    想一想会发现,不管怎么倒,计量单位都是GCD(A,B)的倍数,则可得出Min=GCD(A,B)。
    我拓欧还不会啊啊啊!
    拓展欧几里得定理:对于两个不全为0的整数A、B,必存在一组解x、y,使得Ax+By=GCD(A,B)。
    转换为算法易得:GCD(A,B)=GCD(B,A%B)=......
    且有:Ax+By=Bx+(A%B)y=......(每个式子中的x、y都不相同)
    进行转化,Bx+(A%B)y=Bx+[A-(A/B)B]y=Ay+B[x-(A/B)y],类似于上一层Ax+By的形式。
    可推出,对于递归层数之间的x、y关系为:
        ①x1=y2
        ②y1=x2-(A/B)y2
    回溯到最后,便可以得出对于Ax+By=GCD(A,B)的一组解。
    由此算法进行推广,对于一般的方程Ax+By=C,可得其一组解为:
        ①X=x(C/GCD(A,B))
        ②Y=y(C/GCD(A,B))
    还可推得,对于所有的解(T为任意整数):
        ①X=x+B/GCD(A,B)T
        ②Y=y-A/GCD(A,B)T
    此之谓拓展欧几里得算法。
*/
posted @ 2016-10-26 16:32  前前前世。  阅读(290)  评论(0编辑  收藏  举报