编程思维题目-对喝汽水问题的解法整理
一、 问题引入
有这样一道智力题:“某商店规定:三个空汽水瓶可以换一瓶汽水。小张手上有十个空汽水瓶,她最多可以换多少瓶汽水喝?”答案是5瓶,方法如下:先用9个空瓶子换3瓶汽水,喝掉3瓶满的,喝完以后4个空瓶子,用3个再换一瓶,喝掉这瓶满的,这时候剩2个空瓶子。然后你让老板先借给你一瓶汽水,喝掉这瓶满的,喝完以后用3个空瓶子换一瓶满的还给老板。如果小张手上有n个空汽水瓶,最多可以换多少瓶汽水喝?
有一位同学给出了如下图所示的算法
那么问题来了,这样写到底正确吗???????
二、 实际操作
将上图代码运行一下
1. public class Test02 { 2. public static int empty(int x){ 3. if (x>3){ 4. return x+empty(x/3+x%3); 5. } 6. if (x==2){ 7. return 1; 8. } 9. return 0; 10. } 11. 12. public static void main(String[] args) { 13. System.out.println(empty(10)-10); 14. } 15. } |
结果确实是5
三、 理论分析
(一) 算法描述
换汽水的过程描述如下
1. (拿着空瓶子)手里有n个空瓶子,0瓶未喝的汽水
2. (换汽水)换n/3个汽水,余下n%3个空瓶子
3. (喝汽水)喝掉换来的汽水,手里有n/3+n%3个空瓶子,0瓶未喝的汽水
4. (判断)如果现在手里拿的空瓶子>2,执行1
如果手里拿的空瓶子==2,再换一瓶汽水,结束
手里拿的空瓶子<2,结束
(二) 实例演示算法
手里有10个空瓶子的计算过程如下
现在手里有的空瓶子 |
现在手里有的汽水数 |
喝在肚子里的汽水数 |
10 |
0 |
|
10%3=1 |
10/3=3 |
|
10%3+10/3=4 |
0 |
3 |
4%3=1 |
4/3=1 |
|
4%3+4/3=2 |
0 |
1 |
现在手里有的汽水数为0代表把用空瓶换来的汽水喝掉了
最后剩2个空瓶子,还能换一瓶汽水
喝在肚子汽水数 = 3+1+1 = 5
(三) 问题中提到的程序运算过程
empty(10)-10
= 10+empty(4)-10
= 10+4+empty(2)-10
= 10+4+1-10
根据分析,汽水数应该 = 3+1+1 ,而不是= 10+4+1-10
显然虽然程序运行的结果和正确答案相同,但是计算过程很可能有问题
四、 进一步验证程序
上面的分析只是表达原代码的计算过程可能有问题,下面我们用手里20个空瓶子的情况再进一步验证程序
现在手里有的空瓶子 |
现在手里有的汽水数 |
喝在肚子里的汽水数 |
20 |
0 |
|
20%3=2 |
20/3=6 |
|
20%3+20/3=8 |
0 |
6 |
8%3=2 |
8/3=2 |
|
8%3+8/3=4 |
0 |
2 |
4%3=1 |
4/3=1 |
|
4%3+4/3=2 |
0 |
1 |
因为最后剩2个空瓶子,还能再换一瓶汽水
喝在肚子里的汽水数 = 6+2+1+1 = 10
其实现在问题就来了,10个空瓶的时候,原程序输出empty(10)-10
那20个空瓶,后面应该减几呢?
我试了一下,empty(20)-10=23,empty(20)-20=13,和我们算出的答案都不一样
五、 解决问题
下面给出两种程序的写法,来实现这个问题
(一) 根据算法描述,用循环解决
public static void main(String[] args) { |
(二) 递归实现
a. 定义函数:int soda(intempty)
b. 函数描述:empty代表手里有的空瓶,返回一共喝了几瓶汽水
c. 递归公式描述:喝总汽水数 = 直接换来的汽水+剩的空瓶还能换的汽水
d. 递归公式:soda = empty/3+soda(empty/ 3 + empty % 3)
private static int soda(int empty) { if (empty > 2) { return empty / 3 + soda(empty / 3 + empty % 3); } if (empty == 2) { return 1; } return 0; } public static void main(String[] args) { int sodaCount = soda(10); System.out.println(sodaCount); } |
六、 结语
1. 卖汽水的老板真好,2个空瓶都给换汽水
2. 小张真能喝汽水