堆叠瓷砖(硬算法)

时间:2015-04-21 18:06:26

标签: java algorithm sorting graph permutation

这是一个来自代码竞赛的问题,我发现难以置信的任何工作算法来解决它。所以我并不是在寻找代码,而是一步一步地解决它的算法。

  

堆叠瓷砖

     

将瓷砖堆放在墙上是Bongani最喜欢的过去之一。他的瓷砖都有相同的厚度,但各不相同   在宽度和高度。 Bongani有N个瓷砖,必须使用它们   根据一组规则给出的序列。他可以放一块瓷砖   只有当它比先前堆叠的更窄时,才能在另一个顶部   瓦。允许Bongani将瓷砖旋转90度   宽度变为高度,高度变为宽度。他也被允许   完全丢弃一块瓷砖。给出一个瓷砖列表,帮助Bongani找到   他可以构建的最高堆栈该示例指定了tile(3,3),   (12,5),(5,8),(6,10)。要获得最高筹码,Bongani会忽略   第一个图块(3,3),因为它小于下一个图块。他用的是   下一个瓷砖(12,5),宽度为12,高度为5。他用   接下来的两个瓷砖,宽度为8,高度为5   6为宽度,10为高度。

我唯一能想到的就是获得每一个可能有效的瓷砖排列并找到最高的排列。 确切的问题可以在这里找到http://www.olympiad.org.za/olympiad/wp-content/uploads/2013/01/2011PO-R2-Questions-0421.pdf(问题5)

3 个答案:

答案 0 :(得分:3)

以下是动态编程解决方案的概述:

你"从左到右移动"并为每个瓷砖找出

  • 我可以通过使用此瓷砖未旋转
  • 来构建多高的塔
  • 使用此瓷砖旋转
  • 可以构建多高的塔
  • 我可以通过不使用此图块来构建高塔

第一个关键观察是每个问题都可以递归回答("如果根据我当前的选择更新当前宽度,我可以为剩余的瓷砖建立多高的塔?")。伪代码:

maxHeight(tiles, currentWidth) {

    // Base case
    if (tiles.isEmpty())
        return 0;  // no tiles -> maxHeight == 0

    int h = 0;
    currentTile = tiles[0]
    remainingTiles = tiles[1...]

    // Compute maxHeight for the case when not using current tile
    h = max(h, maxHeight(remainingTiles, currentWidth)

    // Compute maxHeight when using current tile
    if (currentWidth > currentTile.width)
        subHeight = maxHeight(remainingTiles, currentTile.width)
        h = max(h, subHeight + currentTile.height)

    // Compute maxHeight when using current tile rotated
    if (currentWidth > currentTile.height)
        subHeight = maxHeight(remainingTiles, currentTile.height)
        h = max(h, subHeight + currentTile.width)

    return h
}

第二个关键观察是maxHeight的许多调用具有相同的参数,这意味着可以重用先前的计算。您可以使用memoization或制表(两者都是动态编程的变体)如果您选择使用制表矩阵,它将如下所示:

M[tileN][width] = the height of the tower possible to build from
                  tileN onwards with width 'width'

(正如您可能注意到的width没有明确的上限。这可以通过在开始之前将所有值映射到1, 2, 3, ...来解决。最大宽度将是2N。)

答案 1 :(得分:1)

这是使用动态编程的二次时间算法。设f(i)是塔的最大高度,你可以使用原始方向的第i个块来构建,而不是以后的块。设g(i)是塔的最大高度,你可以用第i个块旋转而不是后来的块来构建。请注意,可以省略块,因此要计算f(i),您必须比所有先前与该方向兼容的f和g值的最大值加1,并且类似于g(i)。最后,答案是所有f(i)和g(i)的最大值。

以下代码显示f的代码。你可以类似地写一个g,或者修改它以获取另一个参数,看看块i是否处于原始方向。

public int f(int i)
{
    if (i == 0)
        return 1;
    if (memoF[i] > 0)
        return memoF[i];
    int maxFound = 1; // using just this block is legal
    for (int j = 0; j<i; j++){
        if (widths[i] < widths[j])
            maxFound = Math.max(f(j)+1,maxFound);
        if (widths[i] < heights[j])
            maxFound = Math.max(g(j)+1,maxFound);
    }
    memoF[i] = maxFound;
    return memoF[i];
}

答案 2 :(得分:0)

我认为这是使用Backtracking轻松有效解决问题的典型示例。

你只是按顺序尝试,先尝试一下你可以做的事情,当你不能继续时,你会回去试试你以前没试过的那个。只是谷歌&#34;数独回溯&#34;有很多页面在解释这一点。

在这种情况下回溯的巨大优势在于,&#34;削减&#34;很多没有意义的scenariou,因此它比尝试检查每个可能的组合更有效。 (就像在数独游戏中一样,回溯过程中,最多的是在1000-10000步中解决,这是非常好的,因为你可以编写的所有可能的数字组合都是~10 ^ 60)