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 此之谓拓展欧几里得算法。 */