【问题标题】:Minimum cost to cut wooden board切割木板的最低成本
【发布时间】:2014-03-25 03:57:39
【问题描述】:

给定一个由 M X N 块木制方形块组成的木板,我们需要找到将木板分成方形木块的最小成本。

我们可以沿水平线和垂直线切割电路板,每次切割都会将电路板分成更小的部分。电路板的每一次切割都有成本,具体取决于切割是沿水平线还是垂直线。

让我们用 x[1], x[2], ..., x[n-1] 表示沿连续垂直线切割它的成本,用 y[1], y[2], ... 沿水平线切割它的成本。 , y[m-1]。如果切割(成本为 c)并经过 n 段,则此切割的总成本将为 n*c。

将整块木板切割成单个正方形的成本是用于将整块木板切割成 1x1 方形木块的连续切割成本的总和。

将整块木板分成 1x1 大小的正方形的最低成本是多少。

示例:让我们采用 6*4 网格。

沿行切割成本如下:[2 1 3 1 4]

按列成本切割如下:[4 1 2]

这里的答案是 42

我们应该依次使用 y5、x1、y3、y1、x3、y2、y4 和 x2 开始切割。第一次切割将是水平切割,其中成本 = y5 = 4。接下来我们将使用 x1 进行垂直切割。由于此切割通过两个线段(由先前的垂直切割创建),因此总成本将为 2*x1 = 2*4。类似地,下一个水平切割 (y3) 将穿过两个段,成本为 2*y3 = 2*3。我们可以以类似的方式进行,得到总成本为 4 + 4*2 + 3*2 + 2*2 + 2*4 + 1*3 + 1*3 + 1*6 = 42。

我的方法:从第 1 行到第 2 行之间的切割开始检查每个切割,然后依此类推。但显然效率太低。那么如何解决这个问题?

【问题讨论】:

    标签: algorithm


    【解决方案1】:

    分割线的选择应该按照成本递减的顺序来实现最小的总成本。

    证明:

    任何“不正确”排序的切割序列必须有 2 个连续切割 C1 和 C2,成本分别为 c1 和 c2,这样 c1

    另一方面,如果 C1 是垂直的,而 C2 是水平的(不失一般性),那么我们可以如下检查它。令 V0 为应用 C1 之前的垂直片段数,H0 为应用 C1 之前的水平片段数。

    • 先申请C1再申请C2的成本为:H0*c1 + (V0+1)*c2
    • 先申请C2再申请C1的成本为:V0*c2 + (H0+1)*c1

    第一个表达式减去第二个得到 c2-c1,它是正数。也就是说,交换 C1 和 C2 可以降低成本。

    结论:使用一系列交换,任何无序序列都可以转换为有序序列,这样总成本只能降低。这样就完成了最优性证明。

    注意:在多个成本相同的情况下,它们的内部排序根本不会影响总成本,如上面的计算所示。

    【讨论】:

    • 如果说一个切割是水平切割,一个切割是垂直切割,那么它不会影响最低成本吗?
    • @user3343643:如果您指的是相同成本的后续削减,那么是的,总成本不会受到影响(c2-c1=0)。
    • 证明有缺陷吗?不胜感激。
    • 嘿 Eyal,感谢您提供简洁的证明,但是您能否解释一下已接受答案中提到的其他排序问题,我认为这不是必需的 -> I am not able to understand why do we need **cut_groups_ordered_by_most_cut_axis**, I am not able to get any test case which actually affected if I don't consider this **cut_groups_ordered_by_most_cut_axis** ordering
    • @codeomnitrix:我同意,似乎 Migol 的解决方案有一个不必要的步骤。我提供的证明表明,交换相同成本的相邻切割不会影响总成本 (delta=c2-c1=0)
    【解决方案2】:

    虽然问题已经得到解答,但我写这篇文章是为了提供一个非正式的、可能是直观的解释:

    我们必须进行 (n-1)*(m-1) 次剪辑,所以我们只需要决定先选择哪个剪辑。

    假设我们必须在成本为 c1 和 c2 的两个切割 C1 和 C2 之间进行选择。让我们说c1>c2。让整个事务的当前总成本记为 C

    如果我们在此步骤之后选择 C1,它将至少(取决于您是否在下一步中添加它)将 c1 添加到整个成本 C。 如果我们在这一步之后选择 C2,它将至少将 c2 添加到整个成本 C。

    所以我们可以说我们现在可以贪婪地选择C1,因为以后选择它会比以后选择C2更糟糕。

    因此,无论切割的类型(水平、垂直)如何,我们都按成本降序进行选择。

    【讨论】:

      【解决方案3】:

      这是我在 Python 2 中的贪婪算法方法。

      1. 为了最大限度地降低成本,必须首先削减成本最高的位置
      2. 切割次数为(M - 1) + (N - 1)
      3. 内部顺序无关紧要。因为最终所有的地点都会被砍掉
      4. 跟踪每个维度 (nx,ny) 中的当前切割数

      代码

      M,N = [int(x) for x in raw_input().split()]
      CY = sorted([int(x) for x in raw_input().split()], reverse=True)
      CX = sorted([int(x) for x in raw_input().split()], reverse=True)
      nx = 1
      ny = 1
      minC = 0
      i = 0
      j = 0
      total = M - 1 + N - 1
      for _ in range(0,total):
          #j == N - 1 to check if CX is exhausted
          if (j == N - 1 or (i != M -1 and CY[i] > CX[j])):
              minC += CY[i]*nx
              ny += 1
              i += 1
          else:
              minC += CX[j]*ny
              nx += 1
              j += 1
      
      print minC%(1000000000+7)
      

      【讨论】:

        【解决方案4】:

        这个问题可以通过贪心算法来解决。

        只有 1 条规则:- 按降序选择切割成本,如果两个或多个成本相等,则选择任何一个。 这是python解决方案:-

        M, N = map(int, raw_input().strip().split(' '))
        Y = map(int, raw_input().strip().split(' '))
        X = map(int, raw_input().strip().split(' '))
        
        Y.sort(reverse=True)
        X.sort(reverse=True)
        
        y_cut = x_cut = 1     #To count the number of segments
        cost = i = j = 0
        
        while i < X.__len__() and j < Y.__len__():
            if X[i] >= Y[j]:
                x_cut = x_cut + 1
                cost = cost + (X[i]*y_cut)
                i = i+1
            else:
                y_cut = y_cut + 1
                cost = cost + (Y[j]*x_cut)
                j = j+1
        
        while i < X.__len__():
            cost = cost + (X[i]*y_cut)
            i = i+1
        
        while j < Y.__len__():
            cost = cost + (Y[j]*x_cut)
            j = j+1
        
        print cost
        

        【讨论】:

          【解决方案5】:

          只需选择切割线以降低成本,这是c++中的代码

          代码:

          #include <bits/stdc++.h>
          using namespace std;
          
          int main() {
          long int t;
          cin >> t;
          while(t--){
              long long int m,n,*x,*y,i,j,sum,cx,cy,modu = 1000000007;
              cin >> m >> n;
              x = new long long int[m-1];
              y = new long long int[n-1];
              for(i=0;i<m-1;i++)
              cin >> x[i];
              for(i=0;i<n-1;i++)
              cin >> y[i];
              sort(y,y+n-1);
              sort(x,x+m-1);
          
              i=m-1-1;sum=0;j=n-1-1;cx = 1;cy =1;
              while(i>=0 && j >=0){
          
                   if(x[i] > y[j]){
                      sum += (x[i]*cy)%modu;
                      cx++;
                      i--;
                  }
                  else{
                      sum += (y[j]*cx)%modu;
                      cy++;
                      j--;
                  }
              }
          
              while(i>=0){
                  sum += (x[i]*cy)%modu;
                  i--;
              }
              while(j>=0){
                  sum += (y[j]*cx)%modu;
                  j--;
              }
          
              cout << sum%modu << endl;
          }
          return 0;
          }
          

          【讨论】:

            【解决方案6】:

            嗯,这似乎很简单。因此,您需要进行所有削减并尽量减少最昂贵的削减数量,您应该按成本排序。

            但有一个问题 - 如果您的削减价格相同,那么您需要将它们分组。假设您必须进行 5 次切割,每个切割 6 次,列 4 次,行 2 次,并且木板未切割。如果你先剪 2 行,你的成本是2 * 6 + 4 * 3 * 6 = 14 * 6。如果你换一种方式,你会得到4 * 6 + 2 * 4 * 6 = 12 * 6

            规则是先进行高价切割,然后沿着大部分切割的轴先切割。

            编辑:要跟踪您有多少段,您需要跟踪您对其他轴所做的切割。 o 如果您对行进行了3 切割,那么切割列将需要3 + 1 切割。如果您已经剪切了5 列和3 行,则剪切另一行将始终必须经过每一列剪切线,这意味着您必须剪切5 + 1 次。

            编辑 2: 由于我的示例不正确,因此使用问题中的情况如下所示:

            cut_x = [2, 1, 3, 1, 4]
            cut_y = [4, 1, 2]
            
            list_of_cuts_grouped_by_cost_descending = [[x5, y1], [x3], [x1, y3], [x2, y2, x4]]
            
            cut_groups_ordered_by_most_cut_axis = [[x5, y1], [x3], [x1, y3], [x2, x4, y2]]
            
            return cut_groups_ordered_by_most_cut_axis.flatten
            

            【讨论】:

            • 请通过一个例子或一些算法伪代码解释一下,使其理解
            • 我认为你误解了这个问题
            • 问题其实很简单——让最具成本效益的切割需要遵循这两条规则。
            • 如何跟踪该剪辑经历了多少段?您上面没有提到吗?
            • 嗨 Migoi,​​我无法理解您为什么需要 cut_groups_ordered_by_most_cut_axis,如果我不考虑这个 cut_groups_ordered_by_most_cut_axis 订购,我无法获得任何实际影响的测试用例跨度>
            猜你喜欢
            • 1970-01-01
            • 2019-02-26
            • 1970-01-01
            • 2020-03-18
            • 1970-01-01
            • 2021-09-06
            • 1970-01-01
            • 1970-01-01
            • 2018-05-04
            相关资源
            最近更新 更多