换啤酒问题
近日微信上有人传防痴呆问题:
2块钱买一瓶啤酒,四个瓶盖可以换一瓶啤酒,两个空瓶可以换一瓶啤酒,十块钱共能喝几瓶啤酒?
这个问题如果心算,如果就直肠子贪心法算的话,主要看记忆力。一般能心算出结果和零头的属于记忆力不错的,年轻人也不一定行。
如果在纸上算,只要不是太粗心的人,都能算出结果。但是学过一定数学的人,都怕这不是最优的结果。
其实假设单一解,贪心地算,这样一道题,结果很简单就是能喝15瓶酒,余下1个空瓶和三个瓶盖。但是如果我们列方程根据这些物品的价值来算,设啤酒价值为a,酒瓶价值为b,瓶盖价值为c,显然方程就是这样:
2¥ = a+b+c
2b = a+b+c
4c = a+b+c
很容易解出:
a = 0.5¥, b = 1¥, c = 0.5¥
这样,如果理想兑换的话(酒瓶和瓶盖能直接折算成钱/酒),10块钱能喝二十瓶啤酒,但这违反了题设的商业模式,显然无法做到。(但如果允许退款,是可以做到的,也是这道题目原题的答案)
貌似最优情况,也至少是要余下一个空瓶和一个瓶盖,但这也是不可能的。这个在数学结构上能够证明(从数学上应该能论证余b+2c和b+c是不可能的),也能用程序进行穷举证明,程序(穷举递归)如下:
1 using System; 2 3 namespace beers 4 { 5 class Program 6 { 7 class Asset 8 { 9 public Asset(int dollars) 10 { 11 Dollars = dollars; 12 } 13 14 public Asset(Asset other) 15 { 16 Beers = other.Beers; 17 Dollars = other.Dollars; 18 Bottles = other.Bottles; 19 Caps = other.Caps; 20 Record = other.Record; 21 } 22 23 public int Beers; 24 public int Dollars; 25 public int Bottles; 26 public int Caps; 27 28 // TODO string builder? it's just a tiny puzzle, don't worry about it 29 public string Record; 30 31 public bool ConsumeDollars() 32 { 33 if (Dollars >= 2) 34 { 35 Dollars -= 2; 36 Beers++; 37 Bottles++; 38 Caps++; 39 Record += string.Format("use 2 dollars\n"); 40 return true; 41 } 42 return false; 43 } 44 45 public bool ConsumeBottles() 46 { 47 if (Bottles >= 2) 48 { 49 Bottles -= 2; 50 Beers++; 51 Bottles++; 52 Caps++; 53 Record += string.Format("use 2 bottles\n"); 54 return true; 55 } 56 return false; 57 } 58 59 public bool ConsumeCaps() 60 { 61 if (Caps >= 4) 62 { 63 Caps -= 4; 64 Beers++; 65 Bottles++; 66 Caps++; 67 Record += string.Format("use 4 caps\n"); 68 return true; 69 } 70 return false; 71 } 72 } 73 74 static Asset Solve(Asset input) 75 { 76 var o1 = new Asset(input); 77 o1 = o1.ConsumeDollars() ? Solve(o1) : null; 78 79 var o2 = new Asset(input); 80 o2 = o2.ConsumeBottles() ? Solve(o2) : null; 81 82 var o3 = new Asset(input); 83 o3 = o3.ConsumeCaps() ? Solve(o3) : null; 84 85 if (o1 != null && (o2 == null || o1.Beers > o2.Beers) && (o3 == null || o1.Beers > o3.Beers)) 86 { 87 return o1; 88 } 89 90 if (o2 != null && (o3 == null || o2.Beers > o3.Beers)) 91 { 92 return o2; 93 } 94 95 if (o3 != null) 96 { 97 return o3; 98 } 99 100 return input; 101 } 102 103 static void Main(string[] args) 104 { 105 var ini = new Asset(10); 106 var opt = Solve(ini); 107 Console.WriteLine("result: {0} beers + {1} bottles {2} caps", opt.Beers, opt.Bottles, opt.Caps); 108 Console.WriteLine("process:\n{0}", opt.Record); 109 } 110 } 111 }
enjoy every minute of an appless, googless and oracless life