二维切割棒算法?

时间:2018-03-03 14:01:14

标签: algorithm dynamic-programming

我有一个使用动态编程解决以下问题的任务:

有一个矩形板和一组给定尺寸和值的矩形元素。任务是将工作表划分为给定尺寸的元素,以便元素的值之和最大。找到这笔钱和一棵随之而来的削减树。

有以下条件:

  • 无法旋转给定元素。
  • 可以削减不限数量的某些类型 元件。
  • 纸张的某些部分可能仍未使用。
  • 切割纸张的唯一可能方法是直线 线,这样你再次获得两个较小的矩形。

问题解决了。解决方案可以在下面找到。

=============================================== ===========================

我理解一个方面的问题,这就是杆切割问题。您将杆分成最小的部分,取第一个并检查是否可以使用给定的部分构建它。记住以这种方式构建零件所获得的重量,然后移动到包含前一个零件的更大部件。你回过你正在尝试的段的长度,并检查使用这个段加上先前构建部分的重量将弥补当前部分的重量的更好总和。

据说,切割木材的问题没有什么不同,但你在中间的某处添加了二维的附加环。不幸的是,我无法想象如何存储值以及如何返回二维值。

我尝试过这样做: 1.循环一维 2.在第二维上循环 3.循环播放您可以使用的所有片段 4.根据1.和2检查您是否可以适合当前段。 5.如果是,请返回段的长度,以查看段的重量+存储的内容是否会给您带来更大的结果;对宽度做同样的事情 6.将结果存储在您当前所在的单元格中 7.浏览数组并找到最佳结果

以下是我在多次调试尝试后生成的代码:

        public int Cut((int length, int width) sheet, (int length, int width, int price)[] elements, out Cut cuts)
    {
            int[,] tmpSheetArr = new int[sheet.length + 1, sheet.width + 1];

            for (int i = 1; i < tmpSheetArr.GetLength(0); i++)
            {
                for (int j = 1; j < tmpSheetArr.GetLength(1); j++)
                {
                    tmpSheetArr[i, j] = Int32.MinValue;
                }
            }

            for (int i = 1; i < tmpSheetArr.GetLength(0); i++)    //columns
            {
                for (int j = 1; j < tmpSheetArr.GetLength(1); j++)    //rows
                {
                    for (int e = 0; e < elements.Length; e++)
                    {
                        (int length, int width, int price) elem = elements[e];
                        if (i >= elem.length && j >= elem.width)
                        {
                            int tmpJ, tmpI, tmpVal;
                            tmpJ = j - elem.width;
                            tmpI = i;
                            while (0 < tmpI)
                            {
                                if(tmpI > i - elem.length && tmpI <= i && tmpJ > j - elem.width && tmpJ <= j)
                                {
                                    tmpJ -= 1;
                                    if (-1 == tmpJ)
                                    {
                                        tmpJ = tmpSheetArr.GetLength(1) - 1;
                                        tmpI -= 1;
                                    }
                                    continue;
                                }

                                tmpVal = tmpSheetArr[tmpI, tmpJ] == Int32.MinValue ? 0 : tmpSheetArr[tmpI, tmpJ];  
                                if (tmpSheetArr[i, j] < elem.price + tmpVal)
                                {
                                    tmpSheetArr[i, j] = elem.price + tmpVal;
                                }

                                tmpJ -= 1;
                                if(-1 == tmpJ)
                                {
                                    tmpJ = tmpSheetArr.GetLength(1) - 1;
                                    tmpI -= 1;
                                }
                            }
                        }
                    }
                }
            }

            int tmpMax = 0;
            for (int i = 1; i < tmpSheetArr.GetLength(0); i++)
            {
                for (int j = 1; j < tmpSheetArr.GetLength(1); j++)
                {
                    if (tmpMax < tmpSheetArr[i, j])
                        tmpMax = tmpSheetArr[i, j];
                }
            }


        cuts = null;  
        return tmpMax;   

    }

它不起作用,在某些情况下会产生太大的结果,并且会遇到更大的问题。我认为主要的问题是回头 - 只有存储的重量我不知道块的大小是多少以及它是否与当前块重叠。

我决定从一开始就写它,但实际上找不到另一种方法。我有一个问题的代码:

        int cutRod(int[] price, int n)
    {
        int[] val = new int[n + 1];
        val[0] = 0;
        int i, j;

        // Build the table val[] and return the last entry
        // from the table
        for (i = 1; i <= n; i++)
        {
            int max_val = Int32.MinValue;
            for (j = 0; j < i; j++)
                max_val = Math.Max(max_val, price[j] + val[i - j - 1]);
            val[i] = max_val;
        }

        return val[n];
    }

如何更改它以使其适用于2D问题?

我试图用最好的方式解释我有限的理解和思考方式。我对此事的任何帮助表示感谢。

2 个答案:

答案 0 :(得分:0)

使x处的动态状态成为一个字典,映射x之后x之前放置的块的特定“天际线”。你从一个平坦的天际线(到目前为止没有街区,干净的边缘)开始,你在另一端寻找一个平坦的天际线(没有离开工作表的边缘)。

当你前进时,你将天际线“降低”1,开始寻找切割新街区的方法,并获得新的天际线。

可能的天际线数量会随着矩形的宽度呈指数增长。

答案 1 :(得分:0)

解决方案:

构建一个最大值数组,可以从给定的1x1尺寸到最大尺寸的板尺寸获得。给定件的最大值存储在[(件的长度)-1的索引下,(件的宽度)-1]。要找到最大值,请检查当前部件如何使用先前的部件和切口形成。

要构建切割树,为当前作品构建第二个最佳切割阵列。当前作品的剪切树的根存储在[(作品的长度)-1的索引下,(作品的宽度) - 1]。

Cuts class:

public class Cut
{

public int length;       // vertical dimension (before cut)
public int width;        // horizontal dimension (before cut)
public int price;        // sum of the values of the two elements resulting from the cut

public bool vertical;    // true for vertical cut, false otherwise
public int n;            // distance from left side (for vertical cut) or top (for horizontal cut) of the current piece
                         // price 0 means there was no cut, topleft and bottomright are null,

public Cut topleft;      // top/left resulting piece after cut
public Cut bottomright;  // bottom/right resulting piece after cut

public Cut(int length, int width, int price, bool vertical=true, int n=0, Cut topleft=null, Cut bottomright=null)
    {
    this.length = length;
    this.width = width;
    this.price = price;
    this.vertical = vertical;
    this.n = n;
    this.topleft = topleft;
    this.bottomright = bottomright;
    }

}

找到最大值的函数和切割树:

        public int Cut((int length, int width) sheet, (int length, int width, int price)[] elements, out Cut cuts)
    {
        int[,] sheetArr = new int[sheet.length, sheet.width];   //contains best values of current pieces that can be formed
        Cut[,] cutsArr = new Cut[sheet.length, sheet.width];    //contains references for cuts used to form pieces of the best value,

        for (int l = 0; l < sheet.length; l++)   //loop on length
        {
            for (int w = 0; w < sheet.width; w++)    //loop on width
            {
                foreach ((int length, int width, int price) elem in elements)   //loop on elements
                {
                    if (elem.length == l + 1 && elem.width == w + 1)    //check if current piece can be build with one of the given elements
                    {
                        sheetArr[l, w] = elem.price;
                        cutsArr[l, w] = new Cut(elem.length, elem.width, elem.price);   //piece is exactly one of the elements (no cut)
                        break;  //no 2 elements of the same size in the given elements
                    }
                    cutsArr[l, w] = new Cut(l + 1, w + 1, 0);    //piece can not be formed from given elements, price = 0 (no cut)
                }

                for (int i = 1; i < Math.Floor((decimal)(l + 1) / 2) + 1; i++)  //go back on length
                {
                    if (sheetArr[i - 1, w] + sheetArr[l - i, w] > sheetArr[l, w])
                    {
                        sheetArr[l, w] = sheetArr[i - 1, w] + sheetArr[l - i, w];

                        cutsArr[l, w] = new Cut(l + 1, w + 1, sheetArr[l, w], false, i, cutsArr[i - 1, w], cutsArr[l - i, w]);
                    }
                }

                for (int i = 1; i < Math.Floor((decimal)(w + 1) / 2) + 1; i++)  //go back on width
                {
                    if (sheetArr[l, i - 1] + sheetArr[l, w - i] > sheetArr[l, w])
                    {
                        sheetArr[l, w] = sheetArr[l, i - 1] + sheetArr[l, w - i];

                        cutsArr[l, w] = new Cut(l + 1, w + 1, sheetArr[l, w], true, i, cutsArr[l, i - 1], cutsArr[l, w - i]);
                    }
                }
            }
        }

        cuts = cutsArr[sheet.length - 1, sheet.width - 1];
        return sheetArr[sheet.length - 1, sheet.width - 1];
    }