向世界分享科学之美,让科学流行起来
问题描述:
在微软亚洲研究院上班,大家早上来的第一件事是干啥呢?查看邮件? No, 是去水房拿饮料:酸奶,豆浆,绿茶、王老吉、咖啡、可口可乐……(当然,还是有很多同事把拿饮料当做第二件事)。管理水房的阿姨们每天都会准备很多的饮料给大家,为了提高服务质量,她们会统计大家对每种饮料的满意度。一段时间后,阿姨们已经有了大批的数据。某天早上,当实习生小飞第一个冲进水房并一次拿了五瓶酸奶、四瓶王老吉、三瓶鲜橙多时,阿姨们逮住了他,要他帮忙。
从阿姨们统计的数据中,小飞可以知道大家对每一种饮料的满意度。阿姨们还告诉小飞, STC( Smart Tea Corp.) 负责给研究院供应饮料,每天总量为 V。 STC 很神奇,他们提供的每种饮料之单个容量都是 2 的方幂,比如王老吉,都是 23 = 8 升的,可乐都是 25 = 32 升的。当然 STC 的存货也是有限的,这会是每种饮料购买量的上限。统计数据中用饮料名字、容量、数量、满意度描述每一种饮料。那么,小飞如何完成这个任务,求出保证最大满意度的购买量呢?
动态规划
对于此题的一个直观解法就是使用动态规划进行求解,是典型的背包问题。具体实现并不复杂,可参考《编程之美》P44-46。
贪心解法
进一步分析题目,可以发现题目中每种饮料的容量均为2 的方幂。利用这一特点,书中提到了如何利用这一性质进行求解的初步介绍。
首先,把信息按照饮料的容量排序。
Volume | TotalCount | Happiness |
20 | TC0_0 | H0_0 |
… | … | … |
20 | TC0_1 | H0_n0 |
… | … | … |
21 | TC1_0 | H0_0 |
… |
| … |
2M | TCM_0 | HM_0 |
… |
|
|
对于总容量V,如果
非零,那么就必然要购买满意度最高的一个1L体积的饮料,对于剩下的(V-1)L进一步求解。下一步求解,就判断是否
非零了,如果是,就对
提供满意度最高的饮料组合。接下去的过程,依次类推,直到购买满容量为V的饮料即为满意度最高的方案。
对于上述解法,网络上的有些关于该问题的文章是此怀疑态度的。然而,本人认为这一种做法是正确的,而且利用这一思路可以极大地简化这一问题的算法复杂度。
因此,接下来我将对这一解法的正确性进行证明,同时给出相应的算法实现,并分析该算法的复杂度。
正确性证明
首先,我们来关注一个
性质
:
性质一:对于任意总容量V,设S为任意其购买的策略(Vol(S)=V,Vol(*)表示采用该策略购买的饮料的容量)。现将V表示成二进制数
,其中
,表示V的二进制表示第i位的值。即
。求证:策略S可进行如下分割
。
证明:对于V=1的情况。显然,是这一结论是正确的。
Vol(S),同时S分割为
。
现假设对于任意V=n-1的情况,上述结论均成立。
那么探讨V=n的情况如下。
对于V=n情况下的策略S,从S中的单独取出任意一个饮料
,设饮料的容量为
。取出后剩余的容量为V'=V-d,而其对应的取饮料策略为
。
根据假设,我们可以知道容量为
V'的情况下,其策略S‘是可以被按题意分割。
记
,则S’可分割如下
,相应的
。
对于容量V‘有下式成立:
从中,我们可以看出
。显然对于
的策略,S’都分割得出来。而S以及S‘则需要分割出
,那么此时只需将
与
的策略合并,即可得
。其他分割的策略保持不变即可将S按题意分割。
由此可的V=n的情况下,按策略S也是可以按题意进行分割。
因此,对于任意大小的V都成立。
性质一得证。
然后,我们回到贪心求解策略。给定任意总容量V,根据性质一,都可以把容量V所采用的策略S按照V的二进制表示进行分割。
为了证明该算法的正确性,我们从容量V的二进制表示的最低位进行讨论。
将策略S按照如下
分割出
,满足
。然后证明:
性质二:对于所有可选择的策略
来选取容量为
的饮料,假设其中的最优选择方案为
。求证总体最优选择策略
一定包含
,即
。
证明:现假设存在某一最优策略
,
并不包含于
。
那么我们可以首先先取出它们交集部分的饮料
。显然,
。
接下来,我们从
剩下的部分选取一部分来构建出新的
容量为
的饮料的策略
。根据性质一,可以证明
是构建得出来的。根据题意
是选取
容量为
的饮料的最优方案。因此,
的满意度一定不大于
。从而,
总能得到一个更优的解。
因此,先对
容量为
的饮料进行决策选择最优解
,总是总体最优解
的一部分。
根据性质二,就容易证明上述贪心算法的正确性了,它每次都取最低位非0值进行决策。满足了最优子结构的性质。