1. 程式人生 > >LeetCode 302. Smallest Rectangle Enclosing Black Pixels

LeetCode 302. Smallest Rectangle Enclosing Black Pixels

分開 min sin pixel rec 思路 val targe ble

DFS

最一開始想到的就是dfs,從題目給的點開始dfs搜索。

時間復雜度為O(mn)

class Solution {
public:
    int i_min=INT_MAX, i_max=INT_MIN;
    int j_min=INT_MAX, j_max=INT_MIN;
    
    vector<vector<int>> dirs={{1,0},{-1,0},{0,1},{0,-1}};
    
    int minArea(vector<vector<char>>& image, int x, int
y) { dfs(image,x,y); return (i_max-i_min+1)*(j_max-j_min+1); } void dfs(vector<vector<char>>& image, int i, int j){ if (i<0 || i>=image.size() || j<0 || j>=image[0].size() || image[i][j]==0) return; i_min=min(i_min,i); i_max=max(i_max,i); j_min
=min(j_min,j); j_max=max(j_max,j); image[i][j] = 0; for (auto dir:dirs){ dfs(image,i+dir[0],j+dir[1]); } } };

Binary Search

這道題給了一個坐標(x,y),其實是有用意的。如果只是單純的搜索,我們完全可以搜索整個圖。

這道題其實可以用二分來做。由於給定的點是黑的,我們可以從水平和豎直兩個方向,分別找到由黑變白的點,和由白變黑的分界點。

其實類似投影的操作,把二維投影為一維的數組,然後從這個一維數組中尋找邊界。而這個投影的過程可以在二分的時候一起完成。

最大的問題是二分。舉個例子,比如 000111 找邊界,我們可以用和 lower_bound 一樣的思路去做。搜索區間 [low,high),解區間 [low,high],

同時,如果當前是投影到水平方向,我們要搜索 image[mid][i] 來判斷這個元素的值;如果是投影到豎直方向,同理。這樣的好處是把投影的過程和二分一起做,否則投影只能先處理好,而生成投影數組時間復雜度為 O(mn)。而這樣和二分一起處理的話,水平和豎直方向時間復雜度分別為 O(mlogn) 和 O(nlogm)。

class Solution {
public:
    int minArea(vector<vector<char>>& image, int x, int y) {
        int m=image.size(), n=image[0].size();
        int left=HorizontalSearch(image,0,y,0,m,true);
        int right=HorizontalSearch(image,y+1,n,0,m,false);
        int top=VerticalSearch(image,0,x,0,n,true);
        int bottom=VerticalSearch(image,x+1,m,0,n,false);
        return (right-left)*(bottom-top);
    }
    
    // [low,high)
    // if whiteToBlack, find the first black, otherwise find the first white
    int HorizontalSearch(vector<vector<char>> &image, int low, int high, int top, int bottom, bool whiteToBlack){
        while (low<high){
            int mid=(low+high)/2, k=top;
            bool isWhite=true;
            while (k<bottom)
                if (image[k][mid]==1) {isWhite=false; break;}
                else ++k;
            if (isWhite==whiteToBlack) low=mid+1;
            else high=mid;
        }
        return low;
    }
        
    int VerticalSearch(vector<vector<char>> &image, int low, int high, int left, int right, bool whiteToBlack){
        while (low<high){
            int mid=(low+high)/2, k=left;
            bool isWhite=true;
            while (k<right)
                if (image[mid][k]==1) {isWhite=false; break;}
                else ++k;
            if (isWhite==whiteToBlack) low=mid+1;
            else high=mid;
        }
        return low;
    }
};

/*
Equivalent:
    if (isWhite==whiteToBlack) 
        low = mid+1;
    else 
        high = mid;

    if (whiteToBlack){
        if (isWhite) low=mid+1;
        else high=mid;
    }else{
        if (isWhite) high=mid;
        else low=mid+1;
    }
*/

其實水平和豎直也可以寫在一起,只要添加一個變量判斷即可,但是為了思路更清晰,我分開來寫。

本題要特別註意二分的寫法,用開區間來做。

時間復雜度 O(mlogn+nlogm)

空間復雜度 O(1)

References:

https://leetcode.com/problems/smallest-rectangle-enclosing-black-pixels/solution/

LeetCode 302. Smallest Rectangle Enclosing Black Pixels