如何找到由四个相同的角形成的二维阵列中的最大矩形?

时间:2018-04-07 14:30:12

标签: javascript algorithm multidimensional-array

考虑这个数组:

[
  ["B", "C", "C", "C", "C", "B", "B", "C", "A", "A"],
  ["B", "A", "C", "B", "B", "A", "B", "B", "A", "A"],
  ["B", "C", "B", "C", "A", "A", "A", "B", "C", "B"],
  ["B", "B", "B", "A", "C", "B", "A", "C", "B", "A"],
  ["A", "A", "A", "C", "A", "C", "C", "B", "A", "C"],
  ["A", "B", "B", "A", "A", "C", "B", "C", "C", "C"],
  ["C", "B", "A", "A", "C", "B", "B", "C", "A", "A"]
]

我正在尝试获取此2D阵列中面积最大的矩形的宽度和高度。答案应该是8 * 4 = 32(坐标(1,1),(1,8),(4,1)和(4,8)),因为它具有相同角落的最大区域{{1} }。

Visual representation of the 2D array: here, the letters are color coded. The A’s are blue, and this largest rectangle, where all four corners are blue, is highlighted.

3 个答案:

答案 0 :(得分:3)

只是因为很有趣:

var l = [ 
["B", "C", "C", "C", "C", "B", "B", "C", "A", "A"],
["B", "A", "C", "B", "B", "A", "B", "B", "A", "A"],
["B", "C", "B", "C", "A", "A", "A", "B", "C", "B"],
["B", "B", "B", "A", "C", "B", "A", "C", "B", "A"],
["A", "A", "A", "C", "A", "C", "C", "B", "A", "C"],
["A", "B", "B", "A", "A", "C", "B", "C", "C", "C"],
["C", "B", "A", "A", "C", "B", "B", "C", "A", "A"]];

var squares = [];
//for each position
for (var column=0; column< l[0].length; column++) 
   for (var row=0; row< l.length ; row++ ) 
    //look for all scuares from this position:
    for (var next_column = column+1; next_column < l[0].length; next_column++ )
        if ( l[row][next_column] == l[row][column] )
            for (var next_row = row+1; next_row < l.length; next_row++)
               //if it is a square
               if (l[next_row][column] == l[row][column] && 
                   l[next_row][next_column] == l[row][column]) 
                    //annotate square
                    squares.push( [ column, row, next_column, next_row  ] );

//get areas from squares
var area_l = squares.map(function(an_square) { 
    return (an_square[2]-an_square[0]+1)*(an_square[3]-an_square[1]+1);
  });

//search for big area
var max_area_index = area_l.indexOf(Math.max(  ...area_l  ));

//here it is
console.log( squares[max_area_index] );
  

结果:数组(4)[1,1,8,4]

答案 1 :(得分:1)

如果我们考虑:

  1. 小矩形具有相同的概率具有与大矩形相同的4个角

  2. 一个点可以是与任何其他点一样多的矩形的角落,无论其位置如何(N = (width-1)*(height-1)

  3. 所以我们确认了直观的算法:我们希望首先寻找更大的矩形,它们更有可能在两侧而不是在中心有角,并在我们找到它们时快速停止

    方法1:按区域严格排列矩形

    此解决方案计算大矩形的形状以便查找它们。

    困难的部分是按区域排序矩形并不像缩小方块或圆圈那么容易。区域12的矩形可以具有[1,12],[2,6],[3,4],[4,3],[6,2]或[12,1]的形状,而区域11的矩形只能是具有[1,11]或[11,1]的形状。

    function getGreatestRectangleStrict () {
        let hMax = tab.length - 1,
            wMax = tab[0].length - 1;
        // Search valid rectangle by decreasing area
        for (let area = (hMax+1)*(wMax+1); area >= 0; --area) {
            // Compute rectangle shapes that fit in tab with given area
            let kMax = Math.min(area, hMax + 1);
            for (let k = 1; k <= kMax ; ++k)
                if (area % k === 0) {
                    let height = k - 1,
                        width = area / k - 1;
                    if ( width <= wMax ) {
                        // New fitting shape found, test rectangles
                        for (let top = 0; top <= hMax - height; ++top)
                            for (let left = 0; left <= wMax - width; ++left) {
                                let bottom = top + height,
                                    right = left + width,
                                    a = tab[top][left];
                                if ( a === tab[bottom][left] &&
                                     a === tab[bottom][right] &&
                                     a === tab[top][right])
                                    // Valid rectangle: stop search
                                    return [top, left, bottom, right];
                            }
                    }
                }
        }
    }
    

    它在小型阵列上表现很好,但在大型阵列上表现不佳。我认为需要一种启发式方法来快速找到形状,并在找到解决方案时仍然停止。

    方法2:混合有效搜索

    此解决方案会查找矩形,并在找到一些大矩形时加速。

    function getGreatestRectangleMixed () {
        let greatestArea = 0;
        let greatestRectangle = [];
    
        // Get horizontal pairs of corners of same color
        // and their vertical positions (y)
        let pairs = {};
        for (let k = 0; k < tab.length; ++k) {
            // Heuristic: alternately search top and bottom
            let y = ( !(k % 2) ? k/2 : tab.length - (k+1)/2);
    
            let line = tab[y];
            if ( line.length * Math.max(tab.length-y,y+1) < greatestArea )
                break; // Heuristic: height too small
    
            for (let i = 0; i < line.length - 1; ++i) {
                let color = line[i];
                for (let j = line.length - 1; j > i; --j) {
                    if ( (j-i+1) * tab.length < greatestArea )
                        break; // Heuristic: width too small
                    if (line[i] === line[j]) {
                        // Pair of corners found
                        let pairKey = i+" "+j;
                        let cornerPair = {
                            corners: [i,j],
                            width: j-i+1,
                            y: y
                        };
                        if (! (color in pairs) )
                            pairs[color] = {};
                        if (! (pairKey in pairs[color]) )
                            // New pair of corners found, keep only first one
                            pairs[color][pairKey] = cornerPair;
                        else {
                            // Rectangle found, check area
                            let isCurrentBottom = pairs[color][pairKey].y < cornerPair.y;
                            let top = (isCurrentBottom ? pairs[color][pairKey] : cornerPair);
                            let bottom = (isCurrentBottom ? cornerPair : pairs[color][pairKey]);
                            let area = top.width * (bottom.y - top.y + 1);
                            if (area > greatestArea) {
                                greatestArea = area;
                                greatestRectangle = [
                                    [top.corners[0], top.y],
                                    [top.corners[1], top.y],
                                    [bottom.corners[0], bottom.y],
                                    [bottom.corners[1], bottom.y]
                                ];
                            }
                        }
                    }
                }
            }
        }
    
        return greatestRectangle;
    }
    

    这个解决方案在OP大小的矩形上比前一个慢一点,但是对于更大的矩形更稳定。

答案 2 :(得分:0)

您可以在依赖数组中获得所有相同的字母位置,并迭代数组以找到最右和最低位置。然后检查另外两个点是否具有所需值。

如果是这样,将找到的rectangel推到临时结果集。稍后对该数组进行排序或直接选择最大区域。

&#13;
&#13;
var array = [["B", "C", "C", "C", "C", "B", "B", "C", "A", "A"], ["B", "A", "C", "B", "B", "A", "B", "B", "A", "A"], ["B", "C", "B", "C", "A", "A", "A", "B", "C", "B"], ["B", "B", "B", "A", "C", "B", "A", "C", "B", "A"], ["A", "A", "A", "C", "A", "C", "C", "B", "A", "C"], ["A", "B", "B", "A", "A", "C", "B", "C", "C", "C"], ["C", "B", "A", "A", "C", "B", "B", "C", "A", "A"]],
    points = {},
    rectangles = [],
    count=0;

array.forEach((a, i) => a.forEach((b, j) => (points[b] = points[b] || []).push({ j, i })));

Object
    .keys(points)
    .forEach(k => {
        points[k].slice(0,-1).forEach((p, m) => {
            var n = points[k].length,
                q;

            while (n--) {
                q = points[k][n];
                if (p.i < q.i && p.j < q.j && k === array[p.i][q.j] && k === array[q.i][p.j]) {
                    rectangles.push({ key: k, top: p.i, left: p.j, bottom: q.i, right: q.j, size: (q.i - p.i + 1) * (q.j - p.j + 1) });
                }
            }
        });
    });

rectangles.sort((a, b) => b.size - a.size);

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