编程之美:第一章游戏之乐-饮料供货问题

书中已经提到了用背包方法解,复杂度为V*N*max(C),空间复杂度为O(v)

这里想了一些其它方法以及谈谈书中提到的贪心算法


方法1: 采用单调队列优化复杂度为O(V*n),空间为2*V

单调队列优化方法参考背包九讲。是可以实现的。但n很大的时候,就不是很好了


方法2:二进制表示法优化复杂度O(V*N*log(C)),空间为V

同样参考背包九讲,对C分解成1,2,4,8,2^k,x,这样任何一个1-C的数字都能有这几个数字构造成。采用01背包可做。

方法2没有方法1好。但是提出这个方法是为了引申出下一个方法。


方法3:合并容量相同的饮料,再采用二进制表示法优化 复杂度O(V*log(V)*log(Sum(C))),空间为V

首先可知log(V)是因为最多只有这么多种容量的饮料,否则2^(log(V))以上的饮料就装不下了

        再有对于同一容量的饮料按幸福值大到小排序,分解出1,2,4,...2^k,x,就是按这个顺序去分组,最多Log(sum(c))组。

当然这个地方需要排序,会有一个nlog(n)的排序复杂度。


方法4:书上提到的贪心算法

显然v可以表示成为二进制数,然后用饮料构造出1,2,4....2^k的容量,去填充这个二进制数即可。

        回顾一下方法3,分组的时候最前面组的由幸福值最高的饮料构成,那么本方法也是,用能够构造出2^k容量的饮料的幸福值最高的饮料去构造,然后去填充

        所以实现方法为,从2^0,2^1,2^2......开始

       step 1: 对于V的第k位是1,那么对于容量为2^k的饮料(包括合并得到的)按幸福值排序,选择幸福值最大的

       step 2: 剩余的饮料合并成新容量为2^(k+1)的饮料,也是贪心的,幸福值最大的一起合并,可能多余1个,扔掉即可(因为肯定用不上了)

       重复step1,2直到得到v的表达

       复杂度分析:

V的二进制表示,那么这里有log(V)的计算

对幸福值排序,提前按容量从小到大,再幸福值从小到大排序,你n*log(n)的复杂度,可知合并操作时(step 2)是对有序的数进行合并,合并的结果也是有序的,不用重排,那么就涉及对合并后2^(k+1)的饮料排序了,归并的思路最坏O(n),所以这一步是O(n*logV)

               还有一个问题就是,合并的过程会不会产生的饮料种类数增加的情况。答案当然是会啊!

               举个例子,v = 16,饮料数量:15,16,16,15第一次合并7,1,7,1,7,1,7,乍一看!数量翻倍了!吓死宝宝了,要怎么办!

              但是仔细想,你合并不就相当于把容量2^k和以下的饮料合并,如果这次饮料总数为N_k,那么你最多出现2*N_k的饮料啊。

              所以合并操作执行以后最多2*N_k的,之前产生的新饮料是会在这个合并过程处理掉的,不会指数上升。给出伪代码:

              ans = 0

             sort( drink,drink  + n)

             for i = 0;  1 >> i <= n; i++

找到drink的区间满足容量是2^i  [a,b]

                       如果v & (1>>i):

                                消耗[a,b]幸福值最大的饮料

                       将[a,b]的饮料合成1>>(i+1)得到新的[a,b]

                       归并合并[c,d],[a,b]的饮料,[c,d]是drink中容量是1>>(i+1)的饮料区间

               最后的复杂度是log(v) + n*log(n) ,实现的时候空间复杂度可能需要2*n

              










  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GDRetop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值