【问题标题】:Algorithm to find the narrowest intervals, m of which will cover a set of numbers找到最窄区间的算法,其中 m 将覆盖一组数字
【发布时间】:2016-04-21 20:16:42
【问题描述】:

假设您有一个 n 个数字的列表。您可以选择 m 个整数(我们将整数称为 a)。对于每个整数 a,删除包含范围 [a - x, a + x] 内的每个数字,其中 x 是一个数字.可以清除列表的 x 最小值是多少?

例如,如果您的数字列表是

1 3 8 10 18 20 25

而 m = 2,答案是 x = 5。

您可以选择两个整数 5 和 20。这将清除列表,因为它会删除 [5-5, 5+5] 和 [20-5, 20+5] 之间的每个数字。

我将如何解决这个问题?我认为解决方案可能与动态编程有关。我不想要暴力破解方法。

代码会很有帮助,最好用 Java、C++ 或 C 编写。

【问题讨论】:

  • 到目前为止你有没有尝试过?
  • 我对这个要求有点困惑。 m 个整数不需要来自列表(例如示例中的整数 5),当您选择它们​​时,您可以充当对手 - 试图找到最差的 m 个整数。因此,x 的最小值必须足够大,以应对您选择的 m 个整数中的最坏情况。但是由于对 m 个整数没有限制,为什么在示例中您没有选择例如-50,000,000,000,000,000 和 +50,000,000,000,000,000,显着增加所需的 x 值?
  • 或者“对手”有点错误? m 个整数的选择是合作的吗?是解决问题的最佳选择吗?
  • 二分搜索forvx,使用经典的贪心覆盖算法验证猜测
  • @BobBilly 你能检查我的答案吗?

标签: java c++ algorithm dynamic-programming knapsack-problem


【解决方案1】:

提示

假设你有这个列表

1 3 8 10 18 20 25

并且想知道如果 x 等于 2 需要多少组来覆盖该集合。

您可以通过选择第一个整数为 1+x(1 是列表中的最小数字)以贪婪的方式解决此问题。这将涵盖最多 1+x+x=5 的所有元素。然后简单地重复这个过程,直到所有数字都被覆盖。

所以在这种情况下,下一个未覆盖的数字是 8,因此我们将选择 8+x=10 并覆盖第二组中直到 10+x=12 的所有数字。

同样,第三组将覆盖 [18,24],第四组将覆盖 [25,29]。

这个 x 的值需要 4 个组。这太多了,所以我们需要增加 x 再试一次。

您可以使用二分法来确定覆盖 m 组中所有数字的 x 的最小值。

【讨论】:

    【解决方案2】:

    递归解决方案:

    首先,你需要一个估计值,你可以分成 m 个组,那么估计值(x)必须是 ~(更大 - 更低元素)/2*m。估计的(x)可能是一个解决方案。如果有更好的解决方案,它在所有组中的 x 都低于 extimated(x)!你可以用第一组检查它,然后递归地重复。问题一直在减少,直到你只有一组:最后一个,你知道你的新解决方案是否更好,如果有更好的,你可以用它来丢弃另一个更糟糕的解决方案。

    private static int estimate(int[] n, int m, int begin, int end) {
        return (((n[end - 1] - n[begin]) / m) + 1 )/2;
    }
    
    private static int calculate(int[] n, int m, int begin, int end, int estimatedX){
        if (m == 1){
            return estimate(n, 1, begin, end);
        } else {
            int bestX = estimatedX;
            for (int i = begin + 1; i <= end + 1 - m; i++) {
                // It split the problem:
                int firstGroupX = estimate(n, 1, begin, i);
                if (firstGroupX < bestX){
                    bestX = Math.min(bestX, Math.max(firstGroupX, calculate(n, m-1, i, end, bestX)));
                } else {
                    i = end;
                }
            }
            return bestX;
        }
    }
    
    public static void main(String[] args) {
        int[] n = {1, 3, 8, 10, 18, 20, 25};
        int m = 2;
        Arrays.sort(n);
        System.out.println(calculate(n, m, 0, n.length, estimate(n, m, 0, n.length)));
    }
    

    编辑:

    长数字版:主要思想,它搜索距离的“岛屿”并将问题分成不同的岛屿。就像分而治之,它将“m”分配到岛屿中。

    private static long estimate(long[] n, long m, int begin, int end) {
        return (((n[end - 1] - n[begin]) / m) + 1) / 2;
    }
    
    private static long calculate(long[] n, long m, int begin, int end, long estimatedX) {
        if (m == 1) {
            return estimate(n, 1, begin, end);
        } else {
            long bestX = estimatedX;
            for (int i = begin + 1; i <= end + 1 - m; i++) {
                long firstGroupX = estimate(n, 1, begin, i);
                if (firstGroupX < bestX) {
                    bestX = Math.min(bestX, Math.max(firstGroupX, calculate(n, m - 1, i, end, bestX)));
                } else {
                    i = end;
                }
            }
            return bestX;
        }
    }
    
    private static long solver(long[] n, long m,  int begin, int end) {
        long estimate = estimate(n, m, begin, end);
        PriorityQueue<long[]> islands = new PriorityQueue<>((p0, p1) -> Long.compare(p1[0], p0[0]));
        int islandBegin = begin;
        for (int i = islandBegin; i < end -1; i++) {
            if (n[i + 1] - n[i] > estimate) {
                long estimatedIsland = estimate(n, 1, islandBegin, i+1);
                islands.add(new long[]{estimatedIsland, islandBegin, i, 1});
                islandBegin = i+1;
            }
        }
        long estimatedIsland = estimate(n, 1, islandBegin, end);
        islands.add(new long[]{estimatedIsland, islandBegin, end, 1});
        long result;
        if (islands.isEmpty() || m < islands.size()) {
            result = calculate(n, m, begin, end, estimate);
        } else {    
            long mFree = m - islands.size();
            while (mFree > 0) {
                long[] island = islands.poll();
                island[3]++;
                island[0] = solver(n, island[3], (int) island[1], (int) island[2]);
                islands.add(island);
                mFree--;
            }
            result = islands.poll()[0];
        }
        return result;
    }
    
    public static void main(String[] args) {
        long[] n = new long[63];
        for (int i = 1; i < n.length; i++) {
            n[i] = 2*n[i-1]+1;
        }
        long m = 32;
        Arrays.sort(n);
        System.out.println(solver(n, m, 0, n.length));
    }
    

    【讨论】:

    • 它适用于我给出的示例,但不适用于老师的较长示例(列表中有数千个数字)。它们太长,无法在此处复制/粘贴。知道为什么这有时不起作用吗?我正在尽力调试这个。这不是本周到期,但如果您能尽快帮助我,那就太好了
    • @BobBilly 已编辑:用于长数字。
    【解决方案3】:

    一个有效的算法可以是(假设列表已排序)->

    1. 我们可以将列表视为“m”个整数组。

    2. 现在为每个组计算“last_element - first_element+1”,并将该值的最大值存储在变量“ans”中。

    3. 现在'x'的值是'ans/2'。

    我希望它非常清楚这个算法是如何工作的。

    【讨论】:

    • 我在考虑列表 1, 2, 3, 4, 5, 6, 7, ... 100。让 m=2。这个例子的算法是如何工作的?
    • 好的。现在两组是 -> [1,2,...50] 和 [51,52,....100],前半部分 50-1=49 和后半部分 100-51=49 ,其中最大值 = 49所以 x=49/2...但我认为我们应该将 'ans' 增加到 'ans'+1 以便 ans=25.. 或使用 'ceil' 函数.. 现在 25-25,25+25, 75- 25, 75+25.. 整个名单都很清楚。
    【解决方案4】:

    我认为这也是集群化的类似问题。例如,您可以使用 k-means 聚类算法:在 m 个类上对初始列表进行分区,并为 x 获得最大大小除以两个获得的类。

    【讨论】:

      【解决方案5】:

      1) 您应该研究关于算法的时间和空间复杂性的最佳情况、平均情况和最坏情况的复杂性。

      2) 我认为 David Pérez Cabrera 的想法是正确的。让我们假设平均情况(如下面的伪代码)

      3) 用 l 表示整数列表

          keepGoing = true
          min_x = ceiling(l[size-1]-l[0])/(2m)
      
          while(keepGoing)
          {
              l2 = l.copy
              min_x = min_x-1
              mcounter = 1
      
              while(mcounter <= m)
              {
                  firstElement = l2[0]
      //  This while condition will likely result in an ArrayOutOfBoundsException
      //  It's easy to fix this.
      
                  while(l2[0] <= firstElement+2*min_x)
                  {   remove(l2[0])   }
                  mcounter = mcounter+1
              }
              if(l2.size>0)
                  keepGoing = false
          }
          return min_x+1
      

      4) 考虑

      l = {1, 2, 3, 4, 5, 6, 7}, m=2 (gives x=2)
      l = {1, 10, 100, 1000, 10000, 100000, 1000000}, m=2
      l = {1, 10, 100, 1000, 10000, 100000, 1000000}, m=3
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多