【问题标题】:Dividing an integer array into K groups such that every group have the smallest range将一个整数数组分成 K 个组,使得每个组的范围最小
【发布时间】:2017-04-26 11:54:03
【问题描述】:

所以,更正式地说,我得到 N 个数字,需要将它们分成 K 个组,这些组中没有一个是空的。每组范围的总和必须最小。例如:

N = 4,K = 2,输入为 {5,3,1,1}。

一种可能的解决方案是 {5,3},{1,1}。范围之和为 2 ((5-3)+(1-1))。

另一种查看方式是 {1,1,3}{5},它也是 2((3-1)+(单个数字的范围是 0))。

范围始终是组中最大数字与组中最小数字之间的差。

当我搜索互联网时,很明显我需要使用动态编程,但我想出的只是 K=2 的解决方案。

有人可以帮忙吗?

【问题讨论】:

  • 组需要包含连续的数字吗?而@Koekje他需要将它分成k组,并且有N个数字,k

标签: algorithm partitioning


【解决方案1】:
  1. 对数组进行排序。
  2. 设am为数组的第m个th元素;那么,令 am = am – am–1 ∀m ∈ [1; N)。减法循环必须向后运行以避免改变 am–1。 a0 ≡ 0.
  3. 对结果数组进行排序,保持元素的初始索引与元素本身的排序顺序相同。
  4. 获取最后的 K–1 个索引:这些索引加上 0,是所搜索组的第一个元素的索引。

【讨论】:

  • 如果数组已经排序,并且 K 很小,你会得到复杂度 O(n * log(n)),但你可以通过找到 K-1 个最佳索引而不排序得到 O(n)减法后
  • O(n) 可以保持即使排序是不可避免的。例如,就地 MSR 基数排序的this is my implementation。如果所有数组元素都具有独立于n 的固定大小(例如 32 位),则此算法是严格线性的。
【解决方案2】:

通过动态规划,您提出了 k = 2 的解决方案。现在考虑如何将其扩展到 k = 3。

假设 f2(n) 返回 k=2 和前 n 个数字的最优解,f3(n) = f2(n-m) + err(n-m, n)。这是扩展它的一种方式。

【讨论】:

    【解决方案3】:

    所以在这个问题中,我们希望最小化组的范围。

    假设我们有数组A = {1,3,5,7,5,2}

    每个数组的最大范围是max[a]-min[a],最小范围是0

    我们可以使用二进制搜索的变体来找到最小范围,这个答案是基于组必须包含连续数字的约束。

    对于二分搜索,我们需要选择由数组的最小和最大范围给出的边界。

    这个伪/java 代码看起来像这样。

    main(){
      int upper = max(A)-min(A); 
      int lower = 0;  
    
    
      while (true) {
        int mid =  upper-lower;  
        int blocks = calculateBlockCount(A, mid); 
        if (blocks < K) {
          upper = mid - 1;
        } 
        else if (blocks > K) {
          lower = mid + 1;
        } 
        else {
          return upper;
          break;
        }
      }
     }
    
        private static int calculateBlockCount(int[] array, int range) {
          int count = 0;
          int[] dumie_array;
          int dumie_array[].add[array[0]];
    
          for (int i = 0; i < array.length; i++) {
            int dumie_array[].add[array[i]]
             if (Func_range(dumie_array) > range) {
               count++;
               dumie_array = array[i];
             } 
             else {
               dumie_array.add(array[i]);
             }
          }
          return count;
        }
    
        private static int Func_range(int[] input) {
           int range = 0;
           range= max(input)-min(input)
           return sum;
         }
    

    希望还有帮助

    我认为这大部分都适用于 java,只有 C++ 所具有的添加功能不存在。 (不想写这么多,做个arraylist。)不过我觉得程序的思路应该很清楚了。
    这都是基于这篇文章 Need explanation for algorithm searching minimal large sum。 这是一个非常相似的问题。

    干杯, 吉斯

    【讨论】:

      【解决方案4】:

      假设您的数据已经排序,您可以在线性时间内完成。

      您可以在min_valuemax_value 之间的轴上查看数据。简单地说,聪明的解决方案不使用不连续的组,因此任何聪明的解决方案都可以表示为轴上的一组 K 段,每个段 [x1, x2] 代表数据中 x1 和 @987654326 之间的所有数字@。

      解决方案的总成本是所有段长度的总和,也是max_value - min_value - (space between all your segments)。您的段之间的空间本身由K - 1“空段”组成,即其中没有您的输入数字,即两个连续输入数字之间的段。你想要的是最大化K - 1 这样的段的长度之和。

      所以你所要做的就是(简单版):

      • 如有必要,对您的输入进行排序
      • 计算所有 i 的 B[i] = A[i+1] - A[i](A 是已排序的输入数组)
      • 检索B的K-1个最大值(还是线性时间,其实可以和上一步同时完成)
      • 这些是您的组的边界,因此您可以再次遍历 A 以创建组本身(现在您已经有了边界)(此步骤也可以与前面的步骤一起完成)

      如果您的不同值的数量大于 K,则您的所有组必然非空。否则,您可以轻松拆分包含相同值重复项的组,以使所有组非空。

      复杂度(如果已经排序):O(n*k)(最多)如果 K 是常数,则为 O(n)。如果没有,只需改进对最佳 K-1 段的搜索,最多得到 O(n log(n))

      正如所写,额外的内存复杂度很容易达到 O(K)

      【讨论】:

        猜你喜欢
        • 2018-07-28
        • 2021-12-18
        • 2019-06-04
        • 2019-09-19
        • 2021-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多