【问题描述】
话说大诗人李白,一生好饮。幸好他从不开车。
一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:
无事街上走,提壶去打酒。
逢店加一倍,遇花喝一斗。
这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。
请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则:babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?请你计算出所有可能方案的个数(包含题目给出的)。
【问题分析】
穷举算法: 问题描述中给出了一种合理序列babaabbabbabbbb,实际程序中处理二进制数据更方便,我们用0代替a,1代替b,前面的字符串序列可表示为101001101101111,最后一位已经确定肯定为1,只要对前14位中0和1的所有排列组合进行穷举即可。在穷举过程中对每种可能序列进行检查,如果符合以下条件则是一种可能的序列:
(1)最终序列中含有十个1(或五个0)。
(2)根据序列推算,最终酒壶中刚好没有酒。
下面的问题就是如何对14位0和1的所有排列组合进行穷举。当然方法有很多,这里介绍一种利用整数转换成二进制数的方法来穷举所有可能性。由五个0和九个1组成的最小二进制数是00000111111111(十进制数511),最大二进制数是11111111100000(十进制数16352),由此确定了整数穷举的范围。在整数穷举过程中,再使用Integer.toBinaryString()方法将整数转换为二进制数,对于转换后的二进制数不足14位的可以前补0。
【程序代码】
1 public class 蓝桥杯_第五届_李白打酒
2 {
3 public static void main(String[] args) {
4 // TODO Auto-generated method stub
5
6 int count=0;//方案个数计数器
7
8 //对所有可能方案进行穷举,0代表店,1代表花
9 for(int i=Integer.parseInt("00000111111111", 2);i<=Integer.parseInt("11111111100000", 2);i++)
10 {
11 //将整数转换为二进制字符串,由于最后肯定是花,所以最后一位连接一个1
12 String str=Integer.toBinaryString(i)+"1";
13
14 //如果字符串不足15位,前补0
15 int t=15-str.length();
16 for(int j=0;j<t;j++)
17 {
18 str="0"+str;
19 }
20 //str=String.format("%015d",Long.parseLong(str));
21
22 //测试字符串中是否含有10个1(即10个花)
23 //符合这个条件才有可能是其中一个解
24 if(ten_one(str))
25 {
26 int jh=2;//酒壶里开始有2斗酒
27
28 //对当前字符串序列按"遇店(0)加一倍,遇花(1)喝一斗"
29 //推算酒壶最后的剩余酒量
30 for(int j=0;j<str.length();j++)
31 {
32 if(str.charAt(j)=='1')
33 {
34 jh-=1;
35 }
36 else
37 {
38 jh*=2;
39 }
40 }
41
42 //如果最后酒壶中没酒,
43 //就是一种可能的方案,计数器加1
44 if(jh==0)
45 {
46 count++;
47 //System.out.println(str);
48 }
49 }
50 }
51 System.out.println(count);
52 }
53
54 //判断字符串中是否含有10个1
55 //是,返回true,否,返回false
56 private static boolean ten_one(String str) {
57
58 int cc=0;
59 for(int i=0;i<str.length();i++)
60 {
61 if(str.charAt(i)=='1')
62 cc++;
63 }
64 if(cc==10)
65 return true;
66 return false;
67 }
68 }
【运行结果】
14
【相关知识】
数制转换
字符串特定字符计数
基数为2的排列组合
【总结】
解决这个问题最难的就是0和1的所有排列组合的穷举,每个位置上的字符变化只有两种情况(0和1,或者问题描述中所说的a和b),程序中使用的方法可以扩展到每个字符位置上的变化更多的情况,如0、1、2三种情况,可以使用三进制,那就在三进制和十进制之间进行转换。x进制转换为十进制的方式是Integer.parseInt("x进制字符串",x),十进制转换为x进制字符串的方法是Integer.toString(十进制整数,x)。
以上方法适合于字符位数比较多的情况,如果就两三位字符长度,那就用两层或三层循环嵌套就可以解决。