求助,一个比多重背包还要复杂一点的问题。

2022-03-19 11:41:09 +08:00
 tmsdy0404

现有商品 N(N<100)类,第 i 类商品的数量为 C[i],单价为 P[i], 即第 i 类商品的总价格为 C[i]*P[i]。 则所有商品的总价格 PN 为:

另有发票 M(M<N)张。 第 k 张发票的金额为 V[k]。 所以发票的总金额 PM 为:

且 PM = PN 。

求如何分配商品,使其总金额刚好对应上每一张发票金额。 (允许有正负 1 元的误差,我也觉得不可理解,但事实就是这样)

^^^^^^^^^^^^^^说人话的分割线^^^^^^^^^^^^^^^^^^^^^^^

上面说的可能不太清楚,我直接举例:

绿色区域就是要求解的值。可能有很多解,只需要求出来使每个发票金额刚好满足就可以。

个人感觉,如果把每一张发票金额去按多重背包问题求最优,不一定能保证所有发票整体最优。 另外,我这个价格也就是背包问题中的体积,是有小数的,难道要全部放大 100 倍来求解吗?

880 次点击
所在节点    问与答
5 条回复
tmsdy0404
2022-03-19 12:55:35 +08:00
坐等大佬解惑~~~~
wa007
2022-03-19 19:10:35 +08:00
相比多重背包,你这个题目一共有 M 个背包,套用多重背包的做法开销实在太大了。
这应该是个业务问题,不是个算法问题吧。
wa007
2022-03-19 19:27:55 +08:00
1. 初始化
1 )把所有商品放入集合 A
2 )把所有发票放入集合 B

2. 迭代
1 )调用多重背包算法,判断当前的集合 A 都可以组成和为哪些金额的发票,输出数组 A_array ,A_array[i] = True 表示 金额为 i 的发票可以由集合 A 中的某些商品求和得到,A_array[j] = False 表示金额 j 的发票不能由 A 中的商品求和得到。
2 )从小到大遍历集合 B 中的发票,假设当前是金额为 i 的发票,判断 A_array[i] 如果是 True ,就把 i 从集合 B 中删除,同时加入 {j - i for j in B if j > i}(因为你下次可以组成金额为 j-i 的发票,然后把 j 删除,i 再放回 B ),再把 A 中对应的商品剔除。如果 A_array[i] = False ,就继续遍历。如果 B 中全都是 False ,就结束。PS:如果你抽到了 j-i ,就要把 j 删除,加入 i ,对每个发票打个标记,表示如果删除当前发票,需要加入哪些发票。
3 )直到 A 或者 B 为空,或 B 中找不到满足条件的发票为止。

时间复杂度就不算了,随机数据的耗时肯定是大大小于最差复杂度的。如果数据量不大,应该是可行的。
tmsdy0404
2022-03-19 19:55:27 +08:00
@wa007 量有点大啊,没办法把集合 A 的所有组合全部弄出来。
这是实际的数据,虽然商品各类不多,但有的商品数量是 40000 个,
单这个商品就有 2 的 40000 次方-1 种组合,这个量级没法弄吧


![quicker_9f6f1ecd-2481-4cfe-8ab7-855eabf3ee7f.png]( https://s2.loli.net/2022/03/19/BvxKHk78uziVUm9.png)
wa007
2022-03-19 20:10:40 +08:00
你看下背包算法,实现第一步不需要「 2 的 40000 次方-1 种组合」,复杂度主要跟 `PN` 有关

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/841431

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX