不好的消息是:这个问题通过子集和问题的规约变得NP难。给定数字 x1,…,xn 和 S,子集和问题的目标是确定是否存在一些 xi 的子集总和为S。我们用容量为 x1,…,xn 的 A-瓶和容量为S和(x1+…+xn-S)的 B-瓶制造,并确定是否足够进行 n 次倒注。
好消息是:任何贪心策略(即选择任意非空A、选择任意未填充的B,倒注直到必须停止)都是2近似算法(即使用的倒注次数最多是最优解的两倍)。最优解至少使用max(|A|,|B|)次倒注,而贪心方法最多使用|A|+|B|次,因为每次贪心做倒注时,要么排空一个A,要么填满一个B,不需要再次倒注。
可能会有一种近似方案(任何ε>0时都是(1 + ε)-近似)。我现在认为更可能有无法近似的结果 - 在这里通常用于获得近似方案的技巧似乎不适用。
以下是一些可能导致实际精确算法的想法。
给定一个解,用左顶点为A
、右顶点为B
的二分图绘制,并在仅当a
倒入b
时从a
到b
添加一条(无向)边。如果该解是最优解,则我声称没有循环 - 否则我们可以消除循环中最小的倒注并替换绕过循环的丢失体积。例如,如果我有下列倒注
a1 -> b1: 1
a1 -> b2: 2
a2 -> b1: 3
a2 -> b3: 4
a3 -> b2: 5
a3 -> b3: 6
然后我可以通过像这样倒入
a1 -> b1
来消除:
a2 -> b1: 4 (+1)
a2 -> b3: 3 (-1)
a3 -> b3: 7 (+1)
a3 -> b2: 4 (-1)
a1 -> b2: 3 (+1)
现在,由于该图没有环,我们可以将边的数量(倾倒量)计算为
|A| + |B| - #(连通分量)
。这里唯一的变量是连通分量的数量,我们希望最大化它。
我认为贪心算法形成的图没有环。如果我们知道最优解的连通分量是什么,我们可以在每个连通分量上使用贪心算法并得到最优解。
解决这个子问题的一种方法是使用动态规划枚举所有A的子集对X和B的子集对Y,使得sum(X) == sum(Y),然后将它们输入到精确覆盖算法中。当然,这两个步骤都是指数级的,但它们在实际数据上可能表现良好。