5

我有以下问题(在我的版本中,有一些限制可能使问题更容易解决,但一般的解决方案也很好):

我有 4 个列表,有 10 个条目。第一个列表包含 0 到 6 之间的整数条目,其他三个包含 0 到 3 之间的条目。我现在必须找到这四个列表中元素的 100 个最佳组合。一种组合由 4 个值的总和组成,每个列表中一个。请注意,我不仅需要知道结果元素的值,还需要知道它们是如何组成的,因为有更多与这些值相关的信息。

示例 1:

list A: 6, 3, 2, 2, 1, 0, 0, 0, 0
list B: 3, 2, 0, 0, 0, 0, 0, 0, 0
list C: 2, 2, 0, 0, 0, 0, 0, 0, 0
list D: 3, 1, 1, 1, 1, 1, 0, 0, 0

在这种情况下,五种最佳组合是:

  • 14(A[0] + B[0] + C[0] + D[0])
  • 14 (A[0] + B[0] + C[1] + D[0])
  • 13 (A[0] + B[1] + C[0] + D[0])
  • 13 (A[0] + B[1] + C[1] + D[0])
  • 12 (A[0] + B[0] + C[0] + D[1])

请注意,我已经对列表的条目进行了排序,因为这可能是大多数解决此问题的算法的第一步。

简单的解决方案

最简单的解决方案包括形成所有 10.000 种可能的组合,然后从中选出一百个最佳组合。甚至不必对 10.000 种可能的组合进行排序。可以首先扫描数组并分析哪个值出现的频率。然后可以在下一次扫描这些值时选择一百个最佳值(及其进一步的属性)。

一个不起作用的解决方案

我想到的另一个想法是:第一个必须对列表进行排序。在每个列表中,我想找到一个索引,它将那些可以对解决方案做出贡献的条目与不能做出贡献的条目分开。当必须在示例 1 中找到四个最佳组合时,例如可以选择列表和 的前两个元素,以及列表BC的第一个元素AD 这将产生:

A: 6
B: 3, 2
C: 3, 2
D: 3

这些子列表的所有组合都将产生最佳解决方案。然而,这并不总是可能的,如以下示例所示:

示例 2:

(这次只有两个列表)

list A: 6, 5, 5, 0, 0, ...
list B: 3, 1, 1, 0, 0, ...

在这里,最好的四个解决方案是

  • 6 + 3
  • 5 + 3
  • 5 + 3
  • 6 + 1

但是,无法使用索引找到此解决方案,索引将四种组合与所有其他组合分开。

4

5 回答 5

3

让我尝试以更简洁的方式重新表述现有的答案(Evgeny Kluev,solendil)。

解决方案是从配置 (0,0,0,0) 开始的最佳优先搜索。搜索树的分支因子为 4(列表的数量)。

在 (0,0,0,0) 处,您知道下一个最佳配置是 (1,0,0,0)、(0,1,0,0)、(0,0,1,0) 之一或 (0,0,0,1)。因此,您将搜索树的这些“叶子”放入按每个配置的好坏排序的优先级队列中。叶子队列是下一个最佳配置候选队列。

然后,您从队列中取出最佳配置以添加到答案列表并更新队列。例如,如果 (0,0,1,0) 是下一个最好的,则将其从队列中取出,然后添加其子项 - (1,0,1,0),(0,1,1,0), (0,0,2,0), (0,0,1,1) - 到队列中。

于 2012-11-12T16:16:37.540 回答
2

该解决方案使用两种数据结构:

  1. 优先队列,其中列表元素的总和是键,列表索引的元组是值。
  2. 包含列表索引元组的哈希集。

算法:

  1. 对每个列表进行排序。
  2. 将包含每个列表的第一个元素的元组放入队列中。
  3. 当哈希集包含少于 100 个元组时,请执行第 4 步和第 5 步。
  4. 从队列中弹出最大的项目,检查哈希集是否包含对应的索引元组(T)。如果找到这样的元组,则重复步骤 4,否则继续步骤 5。
  5. 将元组 T 插入哈希集中。创建 N 个元组(其中 N 是列表的数量),每个元组的 N-1 个索引等于 T 中的对应索引,并且一个索引大一,并将这些元组插入队列中。

可能实现优先级队列(对于这个问题)的最佳方法是使用有序容器(二叉搜索树或跳过列表),首先按列表元素的总和排序,然后按列表索引。这使得不需要单独的哈希集,因为该容器可以在将重复项添加到队列之前检测它们。

于 2012-11-12T13:28:55.373 回答
2

我的回答的先决条件:解决方案表示为 4 位数字。0000(14) 表示取每个列表的第一个元素的解决方案,得分为 14。1052(7) 取第一个列表的第二个,第二个的第一个,第三个的第六个和第四个的第三个,得分为7.

现在,您从 0000(14) 开始构建一棵树,并通过将四个数字中的每一个都加 1 来查找所有叶子,即 1000(11)、0100(13)、0010(14) 和 0001(12)。你最好的总和是 14,0010(14) 成为一个分支(是解决方案的一部分),你计算它的叶子,即 1010(11)、0110(13)、0020(12) 和 0011(12)。所有叶子的最佳总和是 13,您将这些叶子设置为分支,计算它们的叶子等,直到您长出 100 个解决方案。

此方法通过 20 个步骤解决了您的示例问题,计算了 102 个分支和 188 个叶子。

这是一个解决您的问题的(粗略的)Java 程序。

package HighestSums;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class HighestSums {

static int[][] data = {
                {6, 3, 2, 2, 1, 0, 0, 0, 0, 0},
                {3, 2, 0, 0, 0, 0, 0, 0, 0, 0},
                {2, 2, 0, 0, 0, 0, 0, 0, 0, 0},
                {3, 1, 1, 1, 1, 1, 0, 0, 0, 0}
};

static Set<Solution> branches = new HashSet<Solution>();
static Set<Solution> leaves = new HashSet<Solution>();

public static void main(String[] args) {
    Solution s = new Solution();
    leaves.add(s);
    int sum = s.getSum();

    for (int i=0; i<100; i++) {
        System.out.println("======== STEP " + i);
        System.out.println("-- Nb Branches : " + branches.size());
        System.out.println("-- Nb Leaves : " + leaves.size());
        if (branches.size()>100)
            return;
        sum = max(leaves);
        step(sum);
        System.out.println("Sum : " + sum);
        System.out.println("Res\n" + toStr(branches));
        System.out.println("Analyse\n" + toStr(leaves));
    }
}

private static int max(Set<Solution> analyse2) {
    List<Solution> disp = new ArrayList<HighestSums.Solution>();
    disp.addAll(analyse2);
    Collections.sort(disp);
    return disp.get(0).getSum();
}

private static String toStr(Collection<Solution> l) {
    List<Solution> disp = new ArrayList<HighestSums.Solution>();
    disp.addAll(l);
    Collections.sort(disp);
    String res = "";
    for (Solution s : disp)
        res += s.toString()+"\n";
    return res;
}

private static void step(int sum) {
    List<Solution> created = new ArrayList<Solution>();
    for (Iterator<Solution> it = leaves.iterator(); it.hasNext(); ) {
        Solution a = it.next();
        if (a.getSum()==sum) {
            it.remove();
            branches.add(a);
            for (Solution a2 : a.next()) {
                if (branches.contains(a2) || leaves.contains(a2))
                    continue;
                created.add(a2);
            }
        }
    }
    leaves.addAll(created);
}

static class Solution implements Comparable<Solution>{
    int[] ix = new int[4];
    Solution parent;
    public Solution(Solution sol) {
        System.arraycopy(sol.ix, 0, ix, 0, sol.ix.length);
        parent = sol;
    }
    public Solution() {}
    public String toString() {
        String res = "";
        for (int i=0; i<4; i++) 
            res += ix[i]; 
        res += " " + getSum(); 
        if (parent != null) 
            res += " (" + parent.toString() + ")";
        return res;
    }
    public List<Solution> next() {
        List<Solution> res = new ArrayList<Solution>();
        for (int i=0; i<4; i++) {
            if (ix[i]<9) {
                Solution s2 = new Solution(this);
                s2.ix[i]+=1;
                res.add(s2);
            }
        }
        return res;
    }
    private int getSum() {
        int res = 0;
        for (int i=0; i<4; i++) 
            res += data[i][ix[i]]; 
        return res;
    }
    @Override
    public boolean equals(Object obj) {
        Solution s = (Solution)obj;
        for (int i=0; i<4; i++) 
            if (ix[i]!=s.ix[i])
                return false;
        return true;
    }
    @Override
    public int hashCode() {
        return ix[0]+ix[1]*10+ix[2]*100+ix[3]*1000;
    }
    @Override
    public int compareTo(Solution o) {
        return o.getSum() - getSum();
    }
}

}

现在解决方案(所有 102 次出现):

0000 14
0010 14 (0000 14)
0100 13 (0000 14)
0110 13 (0010 14 (0000 14))
0011 12 (0010 14 (0000 14))
0003 12 (0002 12 (0001 12 (0000 14)))
0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))
0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))
0030 12 (0020 12 (0010 14 (0000 14)))
0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))
0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))
0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))
0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))
0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))
0002 12 (0001 12 (0000 14))
0012 12 (0011 12 (0010 14 (0000 14)))
0090 12 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))
0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))
0020 12 (0010 14 (0000 14))
0001 12 (0000 14)
0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))
0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))
1000 11 (0000 14)
0200 11 (0100 13 (0000 14))
0111 11 (0110 13 (0010 14 (0000 14)))
0180 11 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))
0300 11 (0200 11 (0100 13 (0000 14)))
0130 11 (0030 12 (0020 12 (0010 14 (0000 14))))
0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))
0400 11 (0300 11 (0200 11 (0100 13 (0000 14))))
0114 11 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))
0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))
0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14)))))
0600 11 (0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14))))))
0160 11 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))
0700 11 (0600 11 (0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14)))))))
0104 11 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))
0800 11 (0700 11 (0600 11 (0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14))))))))
0009 11 (0008 11 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))))))
0900 11 (0800 11 (0700 11 (0600 11 (0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14)))))))))
0018 11 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))))
1010 11 (0010 14 (0000 14))
0103 11 (0003 12 (0002 12 (0001 12 (0000 14))))
0210 11 (0110 13 (0010 14 (0000 14)))
0140 11 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))
0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14)))))
0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))
0310 11 (0210 11 (0110 13 (0010 14 (0000 14))))
0008 11 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))))
0105 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))
0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14))))))
0710 11 (0610 11 (0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14))))))))
0102 11 (0002 12 (0001 12 (0000 14)))
0610 11 (0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14)))))))
0112 11 (0012 12 (0011 12 (0010 14 (0000 14))))
0190 11 (0090 12 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))))
0910 11 (0810 11 (0710 11 (0610 11 (0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14))))))))))
0810 11 (0710 11 (0610 11 (0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14)))))))))
0101 11 (0100 13 (0000 14))
0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))))
0120 11 (0110 13 (0010 14 (0000 14)))
0150 11 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))
0170 11 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))
0115 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))
0019 11 (0018 11 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))))
0113 11 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))
0022 10 (0012 12 (0011 12 (0010 14 (0000 14))))
2000 10 (1000 11 (0000 14))
3000 10 (2000 10 (1000 11 (0000 14)))
1100 10 (0100 13 (0000 14))
0091 10 (0090 12 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))))
0106 10 (0105 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))))
0041 10 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))
0061 10 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))
0072 10 (0071 10 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))
0033 10 (0023 10 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))
0025 10 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))
0109 10 (0009 11 (0008 11 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))))))
0082 10 (0081 10 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))))
0052 10 (0051 10 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))
0117 10 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))))
0024 10 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))
0031 10 (0030 12 (0020 12 (0010 14 (0000 14))))
0023 10 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))
2010 10 (1010 11 (0010 14 (0000 14)))
3010 10 (2010 10 (1010 11 (0010 14 (0000 14))))
0032 10 (0022 10 (0012 12 (0011 12 (0010 14 (0000 14)))))
1110 10 (0110 13 (0010 14 (0000 14)))
0118 10 (0018 11 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))))
0081 10 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))
0108 10 (0008 11 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))))))
0051 10 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))
0116 10 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))
0062 10 (0061 10 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))
0071 10 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))
0035 10 (0025 10 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))
0034 10 (0024 10 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))
0107 10 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))))
0042 10 (0041 10 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))
0119 10 (0019 11 (0018 11 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))))))
0021 10 (0011 12 (0010 14 (0000 14)))
0092 10 (0091 10 (0090 12 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))))
于 2012-11-12T13:50:21.173 回答
1

只要最大总和较低,这是一个可以正常工作的解决方案。它的复杂性是O(N*M),其中N是列表的数量,M是每个列表的大小。

创建一个二维数组[4,16],其中每个条目都包含此结构的列表:{ Reference, Index }.

例如,从您的示例中,条目 at[1,9]将存储一个Reference到单元格[0,6]Index并将存储对第二个列表中第一项的引用(值3)。它们存储在单元格中,9因为总和为 9,Index+Reference是获取值的路径9。每个单元格存储此类参考组合的列表。

for (int i = 0; i < 4; i++)
{
  foreach (var v in lists[i])
  {
    if (i == 0)
    {
      result[i, v].Add({ Reference = null, Index = v.Index });
      continue;
    }
    for (int j = 15; j >= 0; j--)
    {
      result[i, v + j].Add({ Reference = result[i-1, j].Pointer, Index = v.Index });
    }
  }
}

现在您有 15 个树结构要遍历以寻找最好的 100 个。您查看results[3, 15]并递归遍历引用。如果您的最后一个(第 4 个)列表已排序,那么您可以在添加最后一个值时执行此遍历。其他列表不必排序。

于 2012-11-12T13:06:43.517 回答
0

列表 A: 6, 5, 5, 0, 0, ... 列表 B: 3, 1, 1, 0, 0, ...

在开始数组上:6(来自 listA 的项目)并且未使用元素,该项目索引来自 B(开始 - 0)5 和索引...

从此数组中选择最大值。选定项目的增量索引。

重复直到列表 A 的最后一项设置索引 - (列表 B 中的元素计数)。

于 2012-11-12T13:29:25.280 回答