自定义分区问题

时间:2011-07-21 17:05:52

标签: java algorithm subset-sum

有人可以指导我如何解决这个问题。

我们得到一个集合S,其中包含k个元素。

现在我们必须将集合S划分为x个子集,这样每个子集中元素数量的差异不会超过1,并且每个子集的总和应尽可能彼此接近。

示例1: {10,20,90,200,100}必须分为2个子集

解决方案:{10200} {20,90,100}

总和是210和210

示例2: {1,1,2,1,1,1,1,1,1,6}

解决方案:{1,1,1,1,6} {1,2,1,1,1}

总和是10和6。

3 个答案:

答案 0 :(得分:2)

我分两个阶段看到了可能的解决方案。

第一阶段

首先选择子集数量N. 如果可能,对原始集S进行排序。 按顺序将S中最大的N个数分配到子集1到N. 以相反的顺序从子集中分配下N个最大数字,N为1。 重复,直到所有数字都被分发。

如果您不能对S进行排序,则将每个数字从S分配到具有最少条目和最小总数的子集(或其中一个子集)中。

现在,您应该拥有N个子集,这些子集的大小彼此相差1个,并且总计非常相似。

第二阶段

现在尝试优化您的近似解决方案。 选择最大的总子集L和最小的总子集M.选择L中的数字小于M中的数字,但不要增加两个子集之间的绝对差值。交换这两个数字。重复。并非所有子集对都具有可交换的数字。每次交换都会使子集保持相同的大小。

如果你有很多时间可以进行全面搜索;如果没有,那么就试着挑选几个明显的案例。我会说如果差异没有减少,就不要交换数字;否则你可能会得到一个无限循环。

一旦某些子集中至少有两个成员,您可以交错阶段。

答案 1 :(得分:1)

这个问题没有简单的算法。

查看partition problem也称为easiest hard problem,解决了这两套问题。这个问题是NP-Complete,你应该能够找到所有在网上解决它的算法

我知道你的问题有点不同,因为你可以选择套数,但你可以从前一个解决方案中获得灵感。

例如:

您可以将其转换为一系列线性程序,让k为您集合中元素的数量。

{a1 ... ak} is your set

For i = 2 to k:

   try to solve the following program:
        xjl = 1 if element j of set is in set number l (l <= i) and 0 otherwise

        minimise max(Abs(sum(apxpn) -sum(apxpm)) for all m,n) // you minimise the max of the difference between 2 sets.

        s.t 
        sum(xpn) on n = 1
        (sum(xkn) on k)-(sum(xkm) on k) <= 1 for all m n // the number of element in 2 list are different at most of one element.
        xpn in {0,1}

  if you find a min less than one    then stop
  otherwise continue

end for

希望我的记号清楚。

这个程序的复杂性是指数级的,如果你找到一个多项式的方法来解决这个问题,你会探测P = NP,所以我认为你不能做得更好。


编辑

我看到你发表评论,我误解了对子集大小的限制(我认为这是2套之间的区别) 我没有时间更新它我会在有空的时候做。 sryy


编辑2

我编辑了线性程序,它应该按照要求做的。我刚刚添加了约束。

希望这次问题得到充分理解,即使此解决方案可能不是最佳的

答案 2 :(得分:0)

我不是科学家,所以我尝试两种方法:

对项目进行排序后,然后从两个“结束”开始,先移动到最后一个到实际的集合,然后转到下一个集合,循环;

然后:

  1. 检查集合总和的差异,如果有帮助则改组项目。
  2. 适当编码生成的集合并尝试遗传算法。