算法設計與分析 第八週 最大矩形


1.題目描述

在這裏插入圖片描述


2.選題原因

本週學習了動態規劃的相關知識,對於動態規劃有了一定的瞭解,因此選擇了一道動態規劃的題目,加深自己對於動態規劃相關算法的理解。


3.題目分析及算法

3.1分析

這道題如果按照正常的思路走的話,無非就是從最邊角的點開始遍歷,每次遍歷的一個點,再以這個點開始向兩邊查找,直到遇到0,說明矩形終止,最後比較矩形,得到矩形的大小。
讓我們簡單的看一下這樣的思路下的算法:


3.2算法1.0

初始化最大矩陣:Max = 0;
遍歷矩陣中的所有點
對每一個點(a, b)

  • 以此爲起點,開始向右,向下遍歷,遇到0則停下;記遍歷行爲x,列爲y。最後的矩形爲:(y - b) * (x - a)
    下面我們來分析一下,很顯然,這種方法需要的時間複雜度非常大:對於每一個點,需要的複雜度爲從當前到最低端(最差情況),複雜度應該是O(n^3),顯然是我們不能夠接受的。
    那麼我們有什麼方法可以改進呢?

3.2直方圖算法

考慮一個直方圖
如果我們有一個直方圖,以這個直方圖爲基礎,找直方圖的最大矩形是否好找呢?是非常好找的。我們只需要依次比較即可。那麼我們現在要做的就是如何將這個01矩陣轉化爲直方圖。
其實,畫直方圖也很好實現,考慮直方圖的特性:就是相鄰的1豎着壘起來。對任意一個點來說,自己的值一定是自己頭頂的元素值+1(自己是0的時候除外)
如何實現呢?
從每一行開始計算,將以上的數據承載在本行,舉一個簡單的例子:
該元素爲3,則證明頭上有21,如果元素爲1,則證明頭頂是0,如果該元素是0呢,證明頭頂是0,自己也是0
這樣,在每一行都找尋一遍最大元素,並不斷地更新最大值,即可得到。
那麼還有一個重要的問題,就是怎麼求一個直方圖中的最大值?一個比較普遍的解法就是剪枝法,就不贅述了,主要的思路就是每求一個點,都用這個點和之前的結果去比較,如果不會比之前更大,那麼也不用接着後面的計算,直接跳過即可。
我們的算法可以進一步改進:


3.3算法3.2

維護數組直方圖,初始化爲第一行的元素隊列;
以行爲單位開始遍歷:對於每一行:

  • 遍歷每一個元素,若該元素爲0,則直方圖的該位置爲0,否則爲直方圖該位置元素數值+1
  • 用這一行裏最大的矩陣大小與維護值比較,取大的數。

3.4小例子

我們用一個簡單的小例子來解釋一下這個算法:

  ["1","0","1","0","0"],
  ["1","0","1","1","1"],
  ["1","1","1","1","1"],
  ["1","0","0","1","0"]

對於這樣的一個矩陣我們維護的直方圖應該是怎麼樣的呢?
第一行1, 0, 1, 0, 0
第二行2, 0, 2, 1, 1
第三行3, 1, 3, 2, 2
第四行4, 0, 0, 3, 0


4.關鍵代碼

4.1構建直方圖

        int max = 0;
        int row = matrix.size();
        int col = matrix[0].size();
        vector<int> height(col + 1, 0);
        //從每一行開始遍歷
        for (int i = 0; i < row; i++) {
            stack<int> s;                   //維護當前最高的方塊的位置
            for (int j = 0; j < col + 1; ++j) {
                if (j < col) {
                    height[j] = matrix[i][j] == '1' ? height[j] + 1 : 0;
                }

4.2剪枝比較

                //運用剪枝的方法,每得到一個點,就求到目前的最大值
                //如果沒有最大的點大,那麼可以減去
                while (!s.empty() && height[s.top()] >= height[j]) {
                    int cur = s.top();
                    s.pop();
                    //比較到當前點和維護的最大解
                    int temp_max = height[cur] * (s.empty() ? j : (j - s.top() - 1));
                    max = max >  temp_max? max :temp_max;
                }
                s.push(j);

5.結果

在這裏插入圖片描述


6.源代碼

class Solution {
public:
    int maximalRectangle(vector<vector<char>>& matrix) {
        //去除空矩陣
        if (matrix.size() == 0 || matrix[0].size() == 0) {
            return 0;
        }
        int max = 0;
        int row = matrix.size();
        int col = matrix[0].size();
        vector<int> height(col + 1, 0);
        //從每一行開始遍歷
        for (int i = 0; i < row; i++) {
            stack<int> s;                   //維護當前最高的方塊的位置
            for (int j = 0; j < col + 1; ++j) {
                if (j < col) {
                    height[j] = matrix[i][j] == '1' ? height[j] + 1 : 0;
                }
                //運用剪枝的方法,每得到一個點,就求到目前的最大值
                //如果沒有最大的點大,那麼可以減去
                while (!s.empty() && height[s.top()] >= height[j]) {
                    int cur = s.top();
                    s.pop();
                    //比較到當前點和維護的最大解
                    int temp_max = height[cur] * (s.empty() ? j : (j - s.top() - 1));
                    max = max >  temp_max? max :temp_max;
                }
                s.push(j);
            }
        }
        return max;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章