1. 程式人生 > >[LeetCode] Number Of Corner Rectangles 邊角矩形的數量

[LeetCode] Number Of Corner Rectangles 邊角矩形的數量

Given a grid where each entry is only 0 or 1, find the number of corner rectangles.

corner rectangle is 4 distinct 1s on the grid that form an axis-aligned rectangle. Note that only the corners need to have the value 1. Also, all four 1s used must be distinct.

Example 1:

Input: grid = 
[[1, 0, 0, 1, 0],
 [0, 0, 1, 0, 1],
 [0, 0, 0, 1, 0],
 [1, 0, 1, 0, 1]]
Output: 1
Explanation: There is only one corner rectangle, with corners grid[1][2], grid[1][4], grid[3][2], grid[3][4].

Example 2:

Input: grid = 
[[1, 1, 1],
 [1, 1, 1],
 [1, 1, 1]]
Output: 9
Explanation: There are four 2x2 rectangles, four 2x3 and 3x2 rectangles, and one 3x3 rectangle.

Example 3:

Input: grid = 
[[1, 1, 1, 1]]
Output: 0
Explanation: Rectangles must have four distinct corners.

Note:

  1. The number of rows and columns of grid will each be in the range [1, 200].
  2. Each grid[i][j] will be either 0 or 1.
  3. The number of 1s in the grid will be at most 6000.

這道題給了我們一個由0和1組成的二維陣列,這裡定義了一種邊角矩形,其四個頂點均為1,讓我們求這個二維陣列中有多少個不同的邊角矩形。那麼最簡單直接的方法就是暴力破解啦,我們遍歷所有的子矩形,並且檢驗其四個頂點是否為1即可。先確定左上頂點,每個頂點都可以當作左上頂點,所以需要兩個for迴圈,然後我們直接跳過非1的左上頂點,接下來就是要確定右上頂點和左下頂點了,先用一個for迴圈確定左下頂點的位置,同理,如果左下頂點為0,直接跳過。再用一個for迴圈確定右上頂點的位置,如果右上頂點位置也確定了,那麼此時四個頂點中確定了三個,右下頂點的位置也就確定了,此時如果右上和右下頂點均為1,則結果res自增1,參見程式碼如下:

解法一:

class Solution {
public:
    int countCornerRectangles(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size(), res = 0;
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == 0) continue;
                for (int h = 1; h < m - i; ++h) {
                    if (grid[i + h][j] == 0) continue;
                    for (int w = 1; w < n - j; ++w) {
                        if (grid[i][j + w] == 1 && grid[i + h][j + w] == 1) ++res;
                    }
                }
            }
        }
        return res;
    }
};

我們來看一種優化了時間複雜度的方法,這種方法的原理是兩行同時遍歷,如果兩行中相同列位置的值都為1,則計數器cnt自增1,那麼最後就相當於有了(cnt - 1)個相鄰的格子,問題就轉化為了求cnt-1個相鄰的格子能組成多少個矩形,就變成了初中數學問題了,共有cnt*(cnt-1)/2個,參見程式碼如下:

解法二:

class Solution {
public:
    int countCornerRectangles(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size(), res = 0;
        for (int i = 0; i < m; ++i) {
            for (int j = i + 1; j < m; ++j) {
                int cnt = 0;
                for (int k = 0; k < n; ++k) {
                    if (grid[i][k] == 1 && grid[j][k] == 1) ++cnt;
                }
                res += cnt * (cnt - 1) / 2;
            }
        }
        return res;
    }
};

下面這種解法由熱心網友edyyy提供,最大亮點是將解法二的beat 65%提高到了beat 97%,速度槓槓的,要飛起來了的節奏。在遍歷前一行的時候,將所有為1的位置存入到了一個數組ones中,然後在遍歷其他行時,直接檢測ones陣列中的那些位置是否為1,這樣省去了檢查一些之前行為0的步驟,提高了執行速度,但是也犧牲了一些空間,比如需要ones陣列,算是個trade off吧,參見程式碼如下:

解法三:

class Solution {
public:
    int countCornerRectangles(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size(), res = 0;
        for (int i = 0; i < m - 1; i++) { 
            vector<int> ones;
            for (int k = 0; k < n; k++) if (grid[i][k]) ones.push_back(k);
            for (int j = i + 1; j < m; j++) {
                int cnt = 0;
                for (int l = 0; l < ones.size(); l++) {
                    if (grid[j][ones[l]]) cnt++;
                }
                res += cnt * (cnt - 1) / 2;
            }           
        }
        return res;
    }
};

參考資料: