查找可以服务我所有项目的最小节点数

时间:2019-03-28 11:50:14

标签: algorithm

我有一些商店和一些物品,我想从最小数量的商店中运送所有物品。

对于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;
}

1 个答案:

答案 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

...,任何一家供应商都可以解决问题。


在每种剩余产品达到多个供应商的地步之后,您便进入了启发式领域。我还没有找到一种非常好的解决方案的参考;它使用智能回溯。

计算每种产品的供应商数量,并找到最小值。在此最小集合中选择一种产品。遍历此产品的供应商(以贪心的顺序排序可能会有所帮助),然后依次选择每个产品作为此产品的供应商。从问题空间中删除供应商和产品,然后再次发生。

首先进行此深度操作。跟踪迄今为止找到的最佳解决方案;将该参数作为回溯中的参数传递,因此您也可以斩断任何与现有解决方案深度不相等的分支。


我希望这能使您迈向一个好的解决方案。