问题描述:现在有N元钱,两块钱一瓶,或者直接给你N瓶酒,两个空瓶换一瓶酒,四个瓶盖换一瓶酒,问你最后可以喝多少瓶?如果能借酒呢?
解决思路1:
N元钱,其实就是N/2瓶酒。所以我们在程序中直接使用N瓶酒进行计算,直接使用递归,直到最后余下的酒瓶和瓶盖不够换酒。但是要注意:每次换酒后可能会剩下酒瓶和瓶盖,比如3个酒瓶和5个瓶盖,拿出2个酒瓶和4个瓶盖换完2瓶酒,剩下1个酒瓶和1个瓶盖,需要“存”起来,可以和下一轮新增的酒瓶和瓶盖一起换酒。代码如下
import java.util.Scanner;
public class Cnt_N_Beer {
// 存储每一次换完酒剩余的酒瓶和瓶盖
static int bottle = 0;
static int cap = 0;
static int beer(int n){
// 使用当前的酒+酒瓶+瓶盖计算可以换的酒
int re_n = (bottle+n)/2 + (cap+n)/4;
bottle = (bottle+n)%2;
cap = (cap+n)%4;
// 结束条件是换不了酒,那么直接返回,结束递归
if(re_n == 0){
return n;
}
return n+beer(re_n);
}
public static void main(String[] args) {
Scanner user = new Scanner(System.in);
System.out.print("please input an integer:");
int n = user.nextInt();
int beer_cnt;
// 如果啤酒数量不够两瓶,没得换,直接返回啤酒数量
if((n<=1) && (n>=0)) {
beer_cnt = n;
bottle = n;
cap = n;
System.out.println("beer is " + beer_cnt);
// 如果输入的啤酒数量为负数,提示
}else if (n<0){
System.out.println("beer count can't be negative");
}else {
// 使用beer方法递归计算
beer_cnt = beer(n);
System.out.println("beer is " + beer_cnt);
}
// 输出最后剩余的酒瓶数和瓶盖数
System.out.println("bottle is " + bottle);
System.out.println("cap is " + cap);
}
}
几个测试结果如下
不知道你有没有发现,不管酒的数量是多少,最后剩下的酒瓶都是1个,瓶盖数是3个 ,等会第二种解决方案会用到这个性质。那么我们是不是可以验证一下,是不是真的每一次都会剩余1个酒瓶,3个瓶盖?使用do_while循环验证如下。
public class Cnt_N_Beer {
// 存储每一次换完酒剩余的酒瓶和瓶盖
static int bottle = 0;
static int cap = 0;
static int beer(int n){
// 使用当前的酒+酒瓶+瓶盖计算可以换的酒
int re_n = (bottle+n)/2 + (cap+n)/4;
bottle = (bottle+n)%2;
cap = (cap+n)%4;
// 结束条件是换不了酒,那么直接返回,结束递归
if(re_n == 0){
return n;
}
return n+beer(re_n);
}
public static void main(String[] args) {
int n = 1;
int loop = 500000;
do{
n++;
beer(n);
}while(((bottle==1) && (cap==3)) && (n<loop));
System.out.println(n);
}
}
测试结果如下,每次都是因为达到了循环次数而退出,如果不加循环次数那么它会一直循环计算。说明每次运行beer()方法后都剩余1个酒瓶,3个瓶盖。
解决思路2
两个酒瓶换一瓶酒,四个瓶盖可以换一瓶酒,然后一瓶酒是2元,所以一个瓶盖的价值是5角,一个酒瓶的价值是1元,那么相当于我们花5角钱就可以喝到一瓶酒,结果是这样吗,我们看一看如果是6元钱,首先可以买3瓶酒,根据思路1的运行结果,我们知道最后可以喝到7瓶酒,按照5角一瓶酒,6元钱我们不是可以喝12瓶酒吗?哪里出问题了?
这里就要提到思路1中的最后一句话,每次最后都会剩下1个酒瓶和3个瓶盖,所以我们是有2.5元浪费的,这2.5元换不了酒。所以每次计算需要先减去2.5元,然后再根据5角一瓶,计算最后可以喝多少酒?还要注意:如果钱是11.9,那么只能买5瓶酒,所以要先将输入的浮点数转为2的倍数的整数,也就是说11.9变为10,14.8变为14,代码如下
import java.util.Scanner;
public class Money_N_Beer {
public static void main(String[] args) {
Scanner user = new Scanner(System.in);
System.out.print("please input your money:");
double real_money = user.nextDouble();
// 将输入的浮点数转为整数,如11.9变成11
int int_money = (int)real_money;
/**
* 虽然已经是整数,但是仍然会存在不够买酒而多余1元,
* 所以先除2得到能买酒的数量,然后再乘2转为钱
* 所以此时得到的money只会是偶数 0,2,4,6……
*/
int money = int_money/2*2;
// 如果钱不够2元,买不了酒
if(money<2) {
System.out.println("money is so little, can't buy beer");
// 如果money>=2 && money<4,那么只能买一瓶酒,最后不存在浪费2.5元,要特殊考虑
}else if(money<4){
System.out.println("beer is " + (money/2));
System.out.println("bottle is " + (money/2));
System.out.println("cap is " + (money/2));
}else {
int beer = (int)((money-2.5)/0.5);
System.out.println("beer is " + beer);
System.out.println("bottle is 1");
System.out.println("cap is 3");
}
}
}
几个测试结果如下
拓展:如果可以借酒呢?
这个是网页上经常看到的问题,按照我们上面的测试,不管多少瓶酒(大于1瓶酒),最后剩余的酒瓶都是1个,瓶盖都是3个,如果可以借酒,借1瓶酒,那么喝完酒变为2个酒瓶,4个瓶盖。可以换2瓶酒,酒喝完后有2个酒瓶,2个瓶盖,可以用2个酒瓶接着换1瓶酒,喝完后剩余1个酒瓶,3个瓶盖,结束。也就是借1瓶酒,我们可以喝4瓶酒,除去借的,我们可以多喝3瓶酒。如果可以一直借,那么可以一直喝下去。新增代码如下红色框代码
运行测试结果如下,每一次借酒都会增加3瓶酒