【问题标题】:Cutting algorithm of two dimensional board二维板的切割算法
【发布时间】:2018-05-04 22:24:26
【问题描述】:

我的作业有问题。

给定一块尺寸为m x n 的板,将这块板切割成总价最优的矩形块。一个矩阵给出了从原始未切割电路板到每个可能的电路板尺寸的价格。

考虑带有价格矩阵的2 x 2 板:

3 4

3 6

我们对每次切割都有固定成本,例如1
一段长度1 x 1 值得3
横向长度1 x 2值得4
垂直长度1 x 2值得3
整板值得6

对于这个例子,最佳利润是 9,因为我们将棋盘切成1 x 1 块。每件都值3,我们做了3 剪裁,所以4 x 3 - 3 x 1 = 9

第二个例子:

1 2

3 4

现在我必须考虑所有的解决方案:

  • 41x1件值4x1 - (cost of cutting) 3x1 = 1
  • 2横向1x2 is worth 2x2 - (cost of cutting) 1x1 = 3
  • 2垂直1x2 is worth 3x2 - (cost of cutting) 1x1 = 5->最佳最优利润
  • 1横向1x2 + 2 x (1x1) pieces is worth 2 + 2 - (cost of cutting) 2 = 2
  • 1垂直1x2 + 2 x (1x1) pieces is worth 3 + 2 - (cost of cutting) 2 = 3

我读过很多关于棒切割算法的文章,但我不知道如何解决这个问题。 你有什么想法吗?

【问题讨论】:

  • 尝试在其他STACK EXCHANGE COMMUNITIES中询问,例如“学术界”
  • 我在主帖中添加了第二个示例。

标签: algorithm dynamic-programming


【解决方案1】:

我是在 Python 中完成的。算法是

  • best_val = 当前板值
  • 检查每个水平和垂直切割以获得更好的价值
    • 对于切割点
    • 在形成的两个板上重复出现
    • 如果值的总和 > best_val
    • ...best_val = 总和
    • ...记录切割点和方向
  • 返回结果:best_val、切点和方向

我不确定您需要什么返回值;我回馈了最好的价值和董事会树。对于您的第二个示例,这是

(5, [[2, 1], [2, 1]])

带有调试跟踪的代码(indent 和标记为prints):

indent = ""
indent_len = 3

value = [[1, 2],
         [3, 4]]

def best_cut(high, wide):
    global indent
    print indent, "ENTER", high, wide
    indent += " " * indent_len

    best_val = value[high-1][wide-1]
    print indent, "Default", best_val
    cut_vert = None
    cut_val = best_val
    cut_list = []

    # Check horizontal cuts
    for h_cut in range(1, 1 + high // 2):
        print indent, "H_CUT", h_cut
        cut_val1, cut_list1 = best_cut(h_cut, wide)
        cut_val2, cut_list2 = best_cut(high - h_cut, wide)
            cut_val = cut_val1 + cut_val2

        if cut_val > best_val:
            cut_list = [cut_list1, cut_list2]
            print indent, "NEW H", h_cut, cut_val, cut_list
            best_val = cut_val
            cut_vert = False
            best_h = h_cut

    # Check vertical cuts
    for v_cut in range(1, 1 + wide // 2):
        print indent, "V_CUT", v_cut
        cut_val1, cut_list1 = best_cut(high, v_cut)
        cut_val2, cut_list2 = best_cut(high, wide - v_cut)
        cut_val = cut_val1 + cut_val2

        if cut_val > best_val:
            cut_list = [cut_list1, cut_list2]
            print indent, "NEW V", v_cut, cut_val, cut_list
            best_val = cut_val
            cut_vert = True
            best_v = v_cut

    # Return result of best cut
    # Remember to subtract the cut cost
    if cut_vert is None:
        result = best_val  , [high, wide]
    elif cut_vert:
        result = best_val-1, cut_list
    else:
        result = best_val-1, cut_list

    indent = indent[indent_len:]
    print indent, "LEAVE", cut_vert, result
    return result

print best_cut(2, 2)

两个测试中每一个的输出(利润和削减规模):

(9, [[[1, 1], [1, 1]], [[1, 1], [1, 1]]])

(5, [[2, 1], [2, 1]])

【讨论】:

  • 我需要获得最大的砧板利润。根据价格减去每次切割的所有成本,我们可以将一块板切成不同的小方块或矩形。对于第一个示例,我们的总利润为 9,第二个示例为 5。
  • 对。这为第二个提供了 5 的利润;我没有运行其他测试。
【解决方案2】:

根据您的回答,我准备了自下而上和自上而下的实施方式。

自下而上:

function bottomUp($high, $wide, $matrix){
    $m = [];

    for($h = 0; $h < $high; $h++){
        for($w = 0; $w < $wide; $w++){
           $m[$h][$w] = $matrix[$h][$w];

            if($h == 0 && $w == 0){
                continue;
            }

            for($i = 1; $i < ($h + 1 >> 1) + 1; $i++){
                $m[$h][$w] = max(
                    $m[$h][$w],
                    $m[$i - 1][$w] + $m[$h - $i][$w] - CUT_COST
                );
            }

            for($i = 1; $i < ($w + 1 >> 1) + 1; $i++){
                $m[$h][$w] = max(
                    $m[$h][$w],
                    $m[$h][$i - 1] + $m[$h][$w - $i] - CUT_COST
                );
            }            
        }        
    }

    return $m[$high-1][$wide-1];
}

自上而下:

function getBestCut($high, $wide, $matrix){
    global $checked;

    if(isset($checked[$high][$wide])){
        return $checked[$high][$wide];
    }

    $bestVal = $matrix[$high-1][$wide-1];
    $cutVert = CUT_VERT_NONE;
    $cutVal = $bestVal;
    $cutList = [];

    for($hCut = 1; $hCut < 1 + floor($high/2); $hCut++){
        $result1 = getBestCut($hCut, $wide, $matrix);
        $cutVal1 = $result1[0];
        $cutList1 = $result1[1];
        $result2 = getBestCut($high - $hCut, $wide, $matrix);
        $cutVal2 = $result2[0];
        $cutList2 = $result2[1];        
        $cutVal = $cutVal1 + $cutVal2;

        if($cutVal > $bestVal){
            $cutList = [$cutList1, $cutList2];
            $bestVal = $cutVal;
            $cutVert = CUT_VERT_FALSE;
            $bestH = $hCut;
        }

        $checked[$hCut][$wide] = $result1;
        $checked[$high - $hCut][$wide] = $result2;
    }

    for($vCut = 1; $vCut < 1 + floor($wide/2); $vCut++){
        $result1 = getBestCut($hCut, $vCut, $matrix);
        $cutVal1 = $result1[0];
        $cutList1 = $result1[1];
        $result2 = getBestCut($high, $wide - $vCut, $matrix);
        $cutVal2 = $result2[0];
        $cutList2 = $result2[1];        
        $cutVal = $cutVal1 + $cutVal2;   

        if($cutVal > $bestVal){
            $cutList = [$cutList1, $cutList2];
            $bestVal = $cutVal;
            $cutVert = CUT_VERT_TRUE;
            $bestH = $vCut;
        }      

        $checked[$hCut][$vCut] = $result1;
        $checked[$high][$wide - $vCut] = $result2;        
    }

    if($cutVert == CUT_VERT_NONE){
        $result = [$bestVal, [$high, $wide]];
    }else if($cutVert == CUT_VERT_TRUE){
        $result = [$bestVal - CUT_COST, $cutList];
    }else{
        $result = [$bestVal - CUT_COST, $cutList];
    }

    return $result;
}

请告诉我他们是否正确实施了这种方法?

不知道自顶向下方法的时间复杂度是O(m^2*n^2)吗?

【讨论】:

    【解决方案3】:

    f(h,w) 代表高度为h 和宽度为w 的电路板可实现的最佳总价格,同时降低价格c。那么

    f(h,w) = max(
      price_matrix(h, w),
      f(i, w) + f(h - i, w) - c,
      f(h, j) + f(h, w - j) - c
    )
    for i = 1 to floor(h / 2)
    for j = 1 to floor(w / 2)
    

    这是一个自下而上的 JavaScript 示例,它返回给定价格矩阵的填充表格。答案就在右下角。

    function f(prices, cost){
      var m = new Array(prices.length);
    
      for (let i=0; i<prices.length; i++)
        m[i] = [];
    
      for (let h=0; h<prices.length; h++){
        for (let w=0; w<prices[0].length; w++){
    
          m[h][w] = prices[h][w];
    
          if (h == 0 && w == 0)
            continue;
    
          for (let i=1; i<(h+1>>1)+1; i++)
            m[h][w] = Math.max(
              m[h][w],
              m[i-1][w] + m[h-i][w] - cost
            );
    
          for (let i=1; i<(w+1>>1)+1; i++)
            m[h][w] = Math.max(
              m[h][w],
              m[h][i-1] + m[h][w-i] - cost
            );
        }
      }
    
      return m;
    }
    
    $('#submit').click(function(){
      let prices = JSON.parse($('#input').val());
      let result = f(prices, 1);
      let str = result.map(line => JSON.stringify(line)).join('<br>');
      $('#output').html(str);
    });
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <textarea id="input">[[3, 4],
    [3, 6]]</textarea>
    <p><button type="button" id="submit">Submit</button></p>
    <div id="output"><div>

    【讨论】:

    • 告诉我你为什么在for循环中使用按位运算符?
    • @Tetsu x &gt;&gt; 1floor(x / 2) 相同
    • 那你为什么要剪得又高又宽呢?
    • @Tetsu 如果我们将w = 10 剪切为3,我们会得到(3, 7)。如果我们将w = 10 剪切为7,我们得到(7, 3)。它们是相同的,我们希望避免重复计算。
    【解决方案4】:

    关于问题的一些想法而不是答案:

    很久以前我学习动态规划,但我写了以下伪代码,认为是O(n ^ 2):

    // 'Board'-class not included
    
    val valueOfBoards: HashMap<Board, int>
    
    fun cutBoard(b: Board, value: int) : int {
    
        if (b.isEmpty()) return 0
         if (valueOfBoards[b] > value) {
            return 0;
        } else {
            valueOfBoards[b] = value
        }
    
        int maxValue = Integer.MIN_VALUE
    
        for (Board piece : b.getPossiblePieces()) {
            val (cuttingCost, smallerBoard) = b.cutOffPiece(piece)
            val valueGained: int = piece.getPrice() - cuttingCost
    
            maxValue = Max(maxValue, valueGained + cutBoard(smallerBoard, value + valueGained))
        }
    
        return maxValue;
    }
    

    board 类的实现不是很简单,这里有一些阐述:

    // returns all boards which fits in the current board
    // for the initial board this will be width*height subboards
    board.getPossiblePieces() 
    
    
    // returns a smaller board and the cutting cost of the cut
    // I can see this becoming complex, depends on how one chooses to represent the board.
    board.cutOffPiece(piece: Board)
    

    目前我不清楚cutOffPiece() 是否会破坏算法,因为您不知道如何进行最佳切割。我认为,由于算法会在某些时候从较大的部分变为较小的部分,所以会很好。

    我试图通过将结果存储在 HashMap&lt;Board, price&gt; 之类的东西中并在继续之前将新的板与存储的最佳价格进行比较来解决子问题(相同板)的重新计算。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-09
      • 2011-05-20
      • 2015-04-28
      • 2020-06-17
      • 1970-01-01
      • 2022-11-17
      • 1970-01-01
      相关资源
      最近更新 更多