倒水

  有两个容器,容积分别为A升和B升,有无限多的水,现在需要C升水。 我们还有一个足够大的水缸,足够容纳C升水。起初它是空的,我们只能往水缸里倒入水,而不能倒出。 可以进行的操作是: 把一个容器灌满; 把一个容器清空(容器里剩余的水全部倒掉,或者倒入水缸); 用一个容器的水倒入另外一个容器,直到倒出水的容器空或者倒入水的容器满。     问是否能够通过有限次操作,使得水缸最后恰好有C升水。

输入:三个整数A, B, C,其中 0 < A , B, C <= 1000000000                输出:0或1,表示能否达到要求。

函数头部: c语言:1表示可以,0表示不可以 int can(int a,int b,int c);

c++语言: true表示可以,false表示不可以 bool can(int a,int b,int c);

java语言:true表示可以,false表示不可以 public class Main {public static boolean can(int a,int b,int c); } 

分析

  经典的倒水问题,有不少公司也出了类似的面试题目,有的以选择题形式出现,也有编程题形式出现的,下面做简要的分析:

  对题目做简要的处理分析后,C升水是可以分多次倒入的,假设A > B,那么每次可以倒的水的量为A , B , (A + B) ,( A - B)升水,设置4个因子,分别为x1 ,  x2,  x3,  x, (x1 , x2, x3, x4 属于整数),如果可以使得水缸最后恰好有C升水,那么必然存在整数x1 , x2, x3, x4,使得

          Ax1 + Bx2, +  (A + B)x3  + (A - B)x = C    等式成立;

  对等式做一定的变换,得到公式

          (x1 + x+ x4)A + (x2 + x- x4)B = C ;     --( 1-1 )

  设 x = x1 + x+ x, y = x2 + x- x4 ,  x, y 均为整数;最终得到公式

          xA + yB = C ;                                               --( 1-2 )       

   x1 ,  x2,  x3,  x可以假设为正整数,用几个for循环可以实现,但是时间复杂度太大,为O(N4),题目中给的范围是0 < A , B, C <= 1000000000;整数在十亿范围,显然运行时间肯定会超过 3s ,不符合要求,那有没有更加合适的方法呢,在算法的书里面,有一个算法,与公式( 1-2 ) 不谋而合,是扩展的欧几里德算法,算法描述:

定理:



  对于不完全为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然存在整数对 x,y ,使得 gcd(a,b)=ax+by.



  



  根据欧几里德扩展算法,Gcd(A, B) = Ax + By,求出A和B的最大公约数,如果C能被最大公约数整除Gcd(A, B) 整除,那就可以实现水缸里恰好为C升水;



  那题目就直接转换为求A 、B的最大公约数了,求公约数可以用辗转相除法,代码如下:



#include "stdio.h"
int can(int a,int b,int c){
    int n = a%b;
    while(n!=0){
        a = b;
        b = n;
        n = a%b;
    }
    if (c%b == 0)
        return 1;
    else 
        return 0;
}
int main(){
    int a,b,c;
    printf("输入a,b,c\n");
    scanf("%d%d%d",&a,&b,&c);
    if ((can(a,b,c))){
        printf("yes");
    } else {
        printf("no");
    }
}