我有一些商店和一些物品,我想从最小数量的商店中运送所有物品。
对于Exm:
我有3家商店(s1,s2,s3)和4种商品(p1,p2,p3,p4)。
这些商店有我的商品集的任何子集。
对于Exm。
s1具有(p1,p3);
s2具有(p2,p4);
s3具有(p2,p3,p4);
因此可以提供我所有商品的最低商店应该是:
(s1,s3)。
我已经竭尽全力检查商店的所有可能组合并找出最低限度。但是要花很多时间。
public static void main(String[] args) {
Map<String, Set<String>> buckets = new HashMap<>();
buckets.putIfAbsent("s1", new HashSet<>());
buckets.putIfAbsent("s2", new HashSet<>());
buckets.putIfAbsent("s3", new HashSet<>());
buckets.get("s1").add("p1");
buckets.get("s1").add("p3");
buckets.get("s2").add("p2");
buckets.get("s2").add("p4");
buckets.get("s3").add("p2");
buckets.get("s3").add("p3");
buckets.get("s3").add("p4");
Set<String> allsku = new HashSet<>();
for (String node : buckets.keySet()) {
allsku.addAll(buckets.get(node));
}
Long val = System.currentTimeMillis();
Set<String> result = getBestCmnm(buckets, new HashSet<>(), allsku);
System.out.println(result + " " + (System.currentTimeMillis() - val));
}
static Set<String> getBestCmnm(Map<String, Set<String>> buckets, Set<String> choosedNode, Set<String> remainingSku) {
if (remainingSku.size() == 0) {
return choosedNode;
}
Set<String> result = new HashSet<>();
Set<String> presentNode = new HashSet<>(buckets.keySet());
presentNode.removeAll(choosedNode);
int min = Integer.MAX_VALUE;
for (String node : presentNode) {
if (containAny(buckets.get(node), remainingSku)) {
Set<String> choosedNode1 = new HashSet<>(choosedNode);
choosedNode1.add(node);
Set<String> remainingSku1 = new HashSet<>(remainingSku);
remainingSku1.removeAll(buckets.get(node));
Set<String> val = getBestCmnm(buckets, choosedNode1, remainingSku1);
if (val.size() < min) {
min = val.size();
result = val;
}
}
}
return result;
}
private static boolean containAny(Set<String> from, Set<String> to) {
for (String f : to) {
if (from.contains(f)) {
return true;
}
}
return false;
}
答案 0 :(得分:1)
这是set cover problem,与“顶点覆盖问题”同构。我见过的所有解决方案都使用一个矩阵表示,该矩阵表示哪些项目可以覆盖哪些项目:
p1 p2 p3 p4
s1 x - x -
s2 - x - x
s3 - x x x
首先,请注意,您有两种针对情况的最佳解决方案:(s1,s2)和(s1,s3)。如果您要进行额外的优化以使所有集合大小的总和最小化(作为多个最小集数解决方案之间的决胜局),那么您将面临更大的攻击难题。
扫描解决方案时,请注意“贪婪算法”。它最容易解释,算法复杂度高,并且给出了很好的近似值,但是证明次优是微不足道的。
“贪婪”是通过选择覆盖最大数量产品的集合来衡量的。然后,从问题空间中删除该集合和那些产品,然后重试剩余的问题。
对于您而言,这很简单:s3
涵盖最多-3种产品。您将s3
放入解决方案集中,从需求中删除p2 p3 p4
,现在剩下的是2x1矩阵:
p1
s1 x
s2 -
这给出了解决方案{s1, s3}
。
预处理
对于获得的任何输入,请确保通过一些简单的预处理来减小问题的大小。除非您也需要进行次要优化(最少数量),否则请查找子集:删除属于另一个子集的任何集合。
最重要的是,在每个阶段,您都(作为解决方案的一部分)选择了任何产品的唯一集合。在您发布的示例中,您将立即将s1
放入解决方案中,因为它是p1
的唯一提供者。这也会从问题空间中删除p3
,使您离开
p2 p4
s2 x x
s3 x x
...,任何一家供应商都可以解决问题。
在每种剩余产品达到多个供应商的地步之后,您便进入了启发式领域。我还没有找到一种非常好的解决方案的参考;它使用智能回溯。
计算每种产品的供应商数量,并找到最小值。在此最小集合中选择一种产品。遍历此产品的供应商(以贪心的顺序排序可能会有所帮助),然后依次选择每个产品作为此产品的供应商。从问题空间中删除供应商和产品,然后再次发生。
首先进行此深度操作。跟踪迄今为止找到的最佳解决方案;将该参数作为回溯中的参数传递,因此您也可以斩断任何与现有解决方案深度不相等的分支。
我希望这能使您迈向一个好的解决方案。