懸線法

適用問題
給定一個 nmn*m0101 矩陣 ,求其面積最大的子矩陣,使得這個子矩陣中的每一位的值都爲 00。(或者矩陣限制條件改一下)

複雜度:

懸線法可以在 O(nm)O(n*m)的時間複雜度內解決以上問題。

定義:

在講解中,我們將值爲11的點稱爲“障礙點”。

  1. 懸線,就是一條豎線,這條豎線要滿足其上端點在矩形的上邊界或其上端點的上面是障礙點。
    我們枚舉每個點的懸線,求出其最多能向左和向右擴展到何處,取最大值,就能求出最大子矩陣了。
  2. up[i][j]up[i][j]:爲矩陣上的點(i,j)(i,j)向上的懸線長度
  3. lft[i][j]lft[i][j]:爲點(i,j)(i,j)向左最多擴展而不會碰到障礙點的長度
  4. rgt[i][j]rgt[i][j]:爲點(i,j)(i,j)向右最多擴展而不會碰到障礙點的長度

思路:

我們可以在O(nm)O(n*m)的複雜度內預處理出up,lft,rgtup,lft,rgt數組的值。
但是,僅僅做出預處理是不夠的,我們發現,一條懸線向左擴展的最長距離還取決於lft[iup[i][j]+1][j],lft[iup[i][j]+2[j],....,lft[i1][j]lft[i-up[i][j]+1][j],lft[i-up[i][j]+2[j],....,lft[i-1][j],向右同理。所以,我們在枚舉時對lft[i][j]=max(lft[i][j],lft[i1][j]),rgt[i][j]=min(rgt[i][j],rgt[i1][j])lft[i][j]=max(lft[i][j],lft[i-1][j]),rgt[i][j]=min(rgt[i][j],rgt[i-1][j])
注意,我們在遇到障礙點的時候,不對lftrgtlft和rgt的值進行更新,因爲障礙點使懸線的起點有所不同。

參考代碼:

  int n, m, ans = -100;
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      scanf("%d", &res[i][j]);
      lft[i][j] = rgt[i][j] = j;
      up[i][j] = 1;
    }
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 2; j <= m; j++) {
      if (res[i][j] == 0 && res[i][j - 1] == 0) {
        lft[i][j] = lft[i][j - 1];//預處理左邊界
      }
    }
    for (int j = m; j >= 1; j--) {
      if (res[i][j] == 0 && res[i][j + 1] == 0){
        rgt[i][j] = rgt[i][j + 1];//預處理右邊界
      }
    }
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      if (i > 1 && res[i][j] == 0 && res[i - 1][j] == 0) {
        lft[i][j] = max(lft[i][j], lft[i - 1][j]);
        rgt[i][j] = min(rgt[i][j], rgt[i - 1][j]);
        up[i][j] = up[i - 1][j] + 1;
      }
      ans = max(ans, (rgt[i][j] - lft[i][j] + 1) * up[i][j]);
    }
  }

例題1:

例題2:

例題3:

發佈了305 篇原創文章 · 獲贊 222 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章