Find largest rectangle containing only zeros in an N×N binary matrix

前端 未结 8 2329
伪装坚强ぢ
伪装坚强ぢ 2020-11-22 06:25

Given an NxN binary matrix (containing only 0\'s or 1\'s), how can we go about finding largest rectangle containing all 0\'s?

Example:

      I
    0          


        
8条回答
  •  自闭症患者
    2020-11-22 06:55

    To be complete, here's the C# version which outputs the rectangle coordinates. It's based on dmarra's answer but without any other dependencies. There's only the function bool GetPixel(int x, int y), which returns true when a pixel is set at the coordinates x,y.

        public struct INTRECT
        {
            public int Left, Right, Top, Bottom;
    
            public INTRECT(int aLeft, int aTop, int aRight, int aBottom)
            {
                Left = aLeft;
                Top = aTop;
                Right = aRight;
                Bottom = aBottom;
            }
    
            public int Width { get { return (Right - Left + 1); } }
    
            public int Height { get { return (Bottom - Top + 1); } }
    
            public bool IsEmpty { get { return Left == 0 && Right == 0 && Top == 0 && Bottom == 0; } }
    
            public static bool operator ==(INTRECT lhs, INTRECT rhs)
            {
                return lhs.Left == rhs.Left && lhs.Top == rhs.Top && lhs.Right == rhs.Right && lhs.Bottom == rhs.Bottom;
            }
    
            public static bool operator !=(INTRECT lhs, INTRECT rhs)
            {
                return !(lhs == rhs);
            }
    
            public override bool Equals(Object obj)
            {
                return obj is INTRECT && this == (INTRECT)obj;
            }
    
            public bool Equals(INTRECT obj)
            {
                return this == obj;
            }
    
            public override int GetHashCode()
            {
                return Left.GetHashCode() ^ Right.GetHashCode() ^ Top.GetHashCode() ^ Bottom.GetHashCode();
            }
        }
    
        public INTRECT GetMaximumFreeRectangle()
        {
            int XEnd = 0;
            int YStart = 0;
            int MaxRectTop = 0;
            INTRECT MaxRect = new INTRECT();
            // STEP 1:
            // build a seed histogram using the first row of grid points
            // example: [true, true, false, true] = [1,1,0,1]
            int[] hist = new int[Height];
            for (int y = 0; y < Height; y++)
            {
                if (!GetPixel(0, y))
                {
                    hist[y] = 1;
                }
            }
    
            // STEP 2:
            // get a starting max area from the seed histogram we created above.
            // using the example from above, this value would be [1, 1], as the only valid area is a single point.
            // another example for [0,0,0,1,0,0] would be [1, 3], because the largest area of contiguous free space is 3.
            // Note that at this step, the heigh fo the found rectangle will always be 1 because we are operating on
            // a single row of data.
            Tuple maxSize = MaxRectSize(hist, out YStart);
            int maxArea = (int)(maxSize.Item1 * maxSize.Item2);
            MaxRectTop = YStart;
            // STEP 3:
            // build histograms for each additional row, re-testing for new possible max rectangluar areas
            for (int x = 1; x < Width; x++)
            {
                // build a new histogram for this row. the values of this row are
                // 0 if the current grid point is occupied; otherwise, it is 1 + the value
                // of the previously found historgram value for the previous position. 
                // What this does is effectly keep track of the height of continous avilable spaces.
                // EXAMPLE:
                //      Given the following grid data (where 1 means occupied, and 0 means free; for clairty):
                //          INPUT:        OUTPUT:
                //      1.) [0,0,1,0]   = [1,1,0,1]
                //      2.) [0,0,1,0]   = [2,2,0,2]
                //      3.) [1,1,0,1]   = [0,0,1,0]
                //
                //  As such, you'll notice position 1,0 (row 1, column 0) is 2, because this is the height of contiguous
                //  free space.
                for (int y = 0; y < Height; y++)
                {
                    if (!GetPixel(x, y))
                    {
                        hist[y]++;
                    }
                    else
                    {
                        hist[y] = 0;
                    }
                }
    
                // find the maximum size of the current histogram. If it happens to be larger
                // that the currently recorded max size, then it is the new max size.
                Tuple maxSizeTemp = MaxRectSize(hist, out YStart);
                int tempArea = (int)(maxSizeTemp.Item1 * maxSizeTemp.Item2);
                if (tempArea > maxArea)
                {
                    maxSize = maxSizeTemp;
                    maxArea = tempArea;
                    MaxRectTop = YStart;
                    XEnd = x;
                }
            }
            MaxRect.Left = XEnd - maxSize.Item1 + 1;
            MaxRect.Top = MaxRectTop;
            MaxRect.Right = XEnd;
            MaxRect.Bottom = MaxRectTop + maxSize.Item2 - 1;
    
            // at this point, we know the max size
            return MaxRect;
        }
    
        private Tuple MaxRectSize(int[] histogram, out int YStart)
        {
            Tuple maxSize = new Tuple(0, 0);
            int maxArea = 0;
            Stack> stack = new Stack>();
            int x = 0;
            YStart = 0;
            for (x = 0; x < histogram.Length; x++)
            {
                int start = x;
                int height = histogram[x];
                while (true)
                {
                    if (stack.Count == 0 || height > stack.Peek().Item2)
                    {
                        stack.Push(new Tuple(start, height));
                    }
                    else if (height < stack.Peek().Item2)
                    {
                        int tempArea = (int)(stack.Peek().Item2 * (x - stack.Peek().Item1));
                        if (tempArea > maxArea)
                        {
                            YStart = stack.Peek().Item1;
                            maxSize = new Tuple(stack.Peek().Item2, (x - stack.Peek().Item1));
                            maxArea = tempArea;
                        }
                        Tuple popped = stack.Pop();
                        start = (int)popped.Item1;
                        continue;
                    }
                    break;
                }
            }
    
            foreach (Tuple data in stack)
            {
                int tempArea = (int)(data.Item2 * (x - data.Item1));
                if (tempArea > maxArea)
                {
                    YStart = data.Item1;
                    maxSize = new Tuple(data.Item2, (x - data.Item1));
                    maxArea = tempArea;
                }
            }
    
            return maxSize;
        }
    

提交回复
热议问题