適用問題
給定一個 的 矩陣 ,求其面積最大的子矩陣,使得這個子矩陣中的每一位的值都爲 。(或者矩陣限制條件改一下)
複雜度:
懸線法可以在 的時間複雜度內解決以上問題。
定義:
在講解中,我們將值爲的點稱爲“障礙點”。
- 懸線,就是一條豎線,這條豎線要滿足其上端點在矩形的上邊界或其上端點的上面是障礙點。
我們枚舉每個點的懸線,求出其最多能向左和向右擴展到何處,取最大值,就能求出最大子矩陣了。 - :爲矩陣上的點向上的懸線長度
- :爲點向左最多擴展而不會碰到障礙點的長度
- :爲點向右最多擴展而不會碰到障礙點的長度
思路:
我們可以在的複雜度內預處理出數組的值。
但是,僅僅做出預處理是不夠的,我們發現,一條懸線向左擴展的最長距離還取決於,向右同理。所以,我們在枚舉時對。
注意,我們在遇到障礙點的時候,不對的值進行更新,因爲障礙點使懸線的起點有所不同。
參考代碼:
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]);
}
}