首先来看一下题目:
有两个容器,容积分别为A升和B升,有无限多的水,现在需要C升水。 我们还有一个足够大的水缸,足够容纳C升水。起初它是空的,我们只能往水缸里倒入水,而不能倒出。 可以进行的操作是: 把一个容器灌满; 把一个容器清空(容器里剩余的水全部倒掉,或者倒入水缸); 用一个容器的水倒入另外一个容器,直到倒出水的容器空或者倒入水的容器满。 问是否能够通过有限次操作,使得水缸最后恰好有C升水。<题目摘自庞果网>
这个问题在程序员面试的时候经常会遇到,下面开始解答这个问题,如果有什么不对之处,还望各位看官斧正。
先来简化一下这个问题:如果c>a+b,那么我们总可以通过倒入n次a和m次b使得c-(na+mb)<a+b,(n、m为非负整数),所以我们只需要研究c<a+b的情况。设x为a、b两数的最大公约数,那么能够量出c的数学条件是c能整除x,即c%x==0.解释一下原因。
因为x是最大公约数,因此根据欧几里得算法必存在两个整数p和q使得x=pa+qb;而且p和q一正一负,不妨设p正q负,那么要想量出x容量的水只需倒满p次a,倒空q次b即可,举例说明设a=25,b=15那么x=5;2b-a=x;也就是说把b倒满两次,每次倒满后倒入a中a满后将a倒空,最后两个杯子中剩余的就是x了。再设a=7,b=5那么x=1;3a-4b=x,换言之就是把a倒满,每次倒满后都倒入b,如果b满了就将b倒空(若a倒满b后a非空,倒空b后把a剩余的倒入b中然后再倒满a),如此倒满3次a,倒空4次b后两容器剩余的就是x了。
下面证明若c不能整除x则不能用a,b容器量出,因为c不能整除x,那么必不存在整数p和q使得c=pa+qb,现在只需证明任何对a,b的操作都可以表示成au+bv,我们看对a和b的操作无非就是倒满a,倒空a,从a导向b,倒满b,倒空b,从b导向a。两个都是满的状态和两个都是空的状态都没有意义;一个满一个空,能进行操作就是从一个倒向另一个,假设a倒像b,那么结果要么a空要么b满,假设b满a非空,那么下一步若把a倒掉,没有意义(因为这又回到了一满一空的状态),所以需要把b倒掉,那么再下一步呢?把a倒满吗?这也没啥意义(因为这还是回到了一空一满的状态)。
通过归纳发现有意义的操作都是把空的倒满,或把满的倒空。而把半空的灌满或者倒空都没意义(任何一个时刻a和b不可能都半空)。也就是说上说6种操作的一个序列,可以加上条件。也就是倒满a的时候要求是a是空的,倒空a的时候要求a是满的。这样任何一个操作过程最终就可以表示为a*u+b*v。
知道了这些,我们就可以很方便的写出程序验证任意给出的三个整数是否可以完成要求。
程序的第一步求a和b的最大公约数x,第二步校验c是否整除x。下面给出两种方式
方式一:
public static boolean run(int a,int b,intc){
int x=0;
c=c%(a+b);//这一步使c<a+b
for(int i=a/2+1;i>0;i--){
if(a%i==0&&b%i==0){
x=i;
break;
}
}
if(c%x==0){return true;}
return false;
}
方式二:
public static boolean run(int a,int b,intc){
c=c%(a+b);//这一步使c<a+b
while(b!=0){
if(a<b){
int temp=a;
a=b;
b=temp;
}
a=a-b;
}
if(c%a==0){return true;}
return false;
}