【问题标题】:dynamic programming and maximum length of a sequence of rectangles that fit into each other相互匹配的矩形序列的动态规划和最大长度
【发布时间】:2026-01-15 14:40:01
【问题描述】:

给定一组n矩形{(L1, W1), (L2, W2), …….., (Ln, Wn)}LiWi分别表示矩形i的长度和宽度。如果Li< LjWi< Wj,我们说矩形i 适合矩形j

我需要帮助设计一个 O( nˆ2 ) 动态规划算法,该算法将找到相互匹配的矩形序列的最大长度。

我尝试了,但在某些情况下它不起作用,如下所示:

LR(i, j)= { 2+ LR(i-1, j-1)  if (Li< Lj and Wi< Wj) or (Lj< Li and Wj< Wi)

             Max ( LR(i+1, j), LR (I, j-1)  otherwise   }

您能帮我改进我的解决方案或找到更好的解决方案吗?

【问题讨论】:

  • 为了利益。我对我的尝试做了一些改进: LR(i, j)= { 1+ LR(i-1, j-1) if (Li

标签: algorithm dynamic-programming rectangles


【解决方案1】:

使用 DP 你可以这样做:

  • 按减小宽度对矩形进行排序,按减小高度对平局进行排序
  • 对于矩形数组中的每个索引,如果该矩形是第一个采用的矩形(包含最外层的矩形),则确定最佳解决方案,并将其存储以供以后查找
  • 使用递归来确定当 当前 矩形被采用 (1) 或不采用 (2) 时可以容纳的最大矩形数。取两者的最大结果。

这是一个 JavaScript 实现,您可以在此处运行:

function maxNumberOfFittingRectangles(rectangles) {
     // Storage of optimal, intermediate results (DP principle), 
     //    keyed by the index of the first rectangle taken
    let memo = new Map;

    // Take a copy of rectangles, and sort it in order of decreasing width, 
    //    and if there are ties: by decreasing height
    rectangles = [...rectangles].sort( (a, b) => (b.width - a.width) 
                                              || (b.height - a.height) );
    
    function recurse(maxHeight, startIndex) {
        for (let i = startIndex; i < rectangles.length; i++) {
            if (rectangles[i].height <= maxHeight ) { // Can fit
                // Try a solution that includes rectangles[i]
                // - Only recurse when we did not do this before
                if (!(memo.has(i))) memo.set(i, recurse(rectangles[i].height, i+1)+1);
                // Also try a solution that excludes rectangles[i], and 
                // return best of both possibilities:
                return Math.max(memo.get(i), recurse(maxHeight, i+1));
            }
        }
        return 0; // recursion's base case
    }
    
    let result = recurse(Infinity, 0);
    // Display some information for understanding the solution:
    for (let i = 0; i < rectangles.length; i++) {
        console.log(JSON.stringify(rectangles[i]), 
                    'if taken as first: solution = ', memo.get(i));
    }
    
    return result;
}

// Sample data
let rectangles = [
    { width: 10, height:  8 },
    { width:  6, height: 12 },
    { width:  4, height:  9 },
    { width:  9, height:  9 },
    { width:  2, height:  9 },
    { width: 11, height:  4 },
    { width:  9, height:  5 },
    { width:  8, height: 11 },
    { width:  6, height:  6 },
    { width:  5, height:  8 },
    { width:  2, height:  7 },
    { width:  3, height:  5 },
    { width: 12, height:  7 },
];

let result = maxNumberOfFittingRectangles(rectangles);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

获取实际的矩形

以上将为您提供最大化的计数,但不是您需要选择哪些矩形来实现该计数。您可以通过构建一个链表来稍微更改算法,在该链表中,您不仅可以保留在给定矩形(按排序顺序)之后可以拾取的矩形的最大数量,而且还可以保留哪个矩形成为下一个选择。

这里是:

function maxNumberOfFittingRectangles(rectangles) {
    let memo = new Map;
    rectangles = [...rectangles].sort( (a, b) => (b.width - a.width) 
                                              || (b.height - a.height) );
    
    function recurse(maxHeight, startIndex) {
        for (let i = startIndex; i < rectangles.length; i++) {
            if (rectangles[i].height <= maxHeight ) { // Can fit
                if (!(memo.has(i))) memo.set(i, recurse(rectangles[i].height, i+1));
                let result = recurse(maxHeight, i+1);
                if (memo.get(i).size < result.size) return result;
                return { next: i, size: memo.get(i).size + 1 };
            }
        }
        return { next: null, size: 0 }; // recursion's base case
    }
    
    let result = recurse(Infinity, 0);
    // Turn linked list into array of rectangles:
    let arr = [];
    while (result.next !== null) {
        arr.push(rectangles[result.next]);
        result = memo.get(result.next);
    }
    return arr;
}

// Sample data
let rectangles = [
    { width: 10, height:  8 },
    { width:  6, height: 12 },
    { width:  4, height:  9 },
    { width:  9, height:  9 },
    { width:  2, height:  9 },
    { width: 11, height:  4 },
    { width:  9, height:  5 },
    { width:  8, height: 11 },
    { width:  6, height:  6 },
    { width:  5, height:  8 },
    { width:  2, height:  7 },
    { width:  3, height:  5 },
    { width: 12, height:  7 },
];

let result = maxNumberOfFittingRectangles(rectangles);

console.log(JSON.stringify(result));
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

    【解决方案2】:

    如果你真的想设计一个 DP 解决方案,你可以考虑以下一个:我们可以应用一个事实,即面积较小的矩形不能适合面积较大的矩形:

    1. 根据矩形的面积 (L x W) 按降序顺序对矩形数组进行排序(面积最大的矩形在索引 0 处)。
    2. 从第一个最大的矩形开始(我们称之为头矩形),然后我们找到这个头矩形具有的所有序列的长度,并从这些长度中得到最大长度(继续阅读)

      一个。头部矩形可能有许多序列。要找到序列,取可以放入头部矩形的较小矩形,调用它们中的每一个 next_head递归 1) 将每个第二个矩形 next_head 作为头部 2) 找到所有子序列这个next_head 有。由于数组已经排序,我们只需要检查next_head之后的矩形。

      b.基本情况:对于一个序列的最后一项,它将没有子序列。然后,return 1,因为这个子序列的长度为 1(头/最后一项本身计为 1)。对于不是一个序列中最后一个的其他项目,它们可能有多个子序列。比较这些子序列返回的长度并取最大值,称为max_length。然后,返回max_length + 1(max_length是它的子序列的最大长度;+1是头部本身)

    3. 现在对其他矩形执行第 2 步,获取每个矩形的最大长度。您可能希望跳过已在另一个序列中计数的矩形。

    Java 代码如下:

    class Rectangle {
      double L;
      double W;
      double area;
    }
    
    public int maxSequences(Rectangle[] rects) {
      // Sort the array based on area. You can do this by yourself
      sortBasedOnArea(rects);
    
      int max = 0;
    
      // Find the maximum length of a sequence of each rectangle: 
      // starting from biggest rectangle to smallest rectangle
      // You can improve this for loop by skipping the rectangles that are already counted in sequence before.
    
      for (int i = 0; i < rects.length; i++) {
    
        // For rectangle at index i,
        // temp_max is the maximum length of sequence 
        // the rectangle can make with other smaller rectangles
        // Simply put, temp_max is the max number of smaller rectangles that can fit into rect[i]
        int temp_max = maxSequencesHelper(rects, i);
    
        if (temp_max > max)
            max = temp_max;
      }
    
      return max;
    }
    
    public int maxSequencesHelper(Rectangle[] rects, int current_head) {
      // Head rectangle
      Rectangle head = rects[current_head];
    
      // Max of sub-sequence rectangles, excluding the head
      int max = 0;
    
      // Loop through smaller rectangles with area < head
      for (int i = current_head + 1; i < rects.length; i++) {
        Rectangle next_rect = rects[i];
    
        if (isFit(head, next_rect)) {
          // If this rectangle can fit into our head,
          // Recursively call this function with this rectangle as the head
    
          int next_head_index = i; // This just improves code readability
          int temp_max = maxSequencesHelper(rects, next_head_index);
          if (temp_max > max)
            max = temp_max;
        }
      }
    
      if (max == 0)
        // max = 0 when the if (isFit) in the for loop never runs
        // Therefore, this rectangle is the last item of this sequence
        return 1;
      else
        return max + 1;
    }
    
    public boolean isFit(Rectangle big, Rectangle small) {
      return small.L < big.L && small.W < big.W;
    }
    

    请注意,DP 可能不是最好的方法,而且我上面的代码也不是最有效的!您可以改进 DP 方法的一件事是存储每个矩形序列的最大长度的解决方案,但是上面捕获了 DP 解决此问题的基本思想。

    如果有什么不清楚的地方,欢迎在下方添加 cmets。

    希望这会有所帮助!

    【讨论】:

    • 我会试试的。谢谢
    【解决方案3】:

    我不确定这里的 DP 算法,但这里是在 O(n^2) 中可以完成的事情:

    【讨论】: