P4147 玉蟾宫

10 篇文章 0 订阅
4 篇文章 0 订阅

01矩陣找最大0子矩陣。

題目地址:玉蟾宫 - 洛谷

目录

Catenary Method

代碼流程

初始化

初始化最左

初始化最右

Core Part

完整代碼

Monotonic Stack

 代碼

初始化

Core Part

完整代碼


經典題目,可以用懸線法(catenary method)或者單調棧,對於所有懸線法可以解的題目,單調棧都可以解,所以不會開一個懸線法系列。

Catenary Method

我們對於每一橫都用兩個指針掃一遍,得到每一橫裡的每一個位置的最大可拓展長度,并把他紀錄在dp的矩陣裡。

然後對於每一列,我們都掃一遍,得到每一個位置最大可向上拓展多少格,并把他紀錄在dp矩陣中。

最後我們進行統計,統計的方法是,我們便利每一行每一列,我們找出每一行最小可向上拓展的長度和最小的寬度相乘就得出面積,如此類推求出最大面積。 為什麼要找最小可向上拓展呢?

因為如果找最長會遇到出錯,因為這一個位置可以拓展到的位置不代表其他也能,找最小是為了保證正確性。

這裡要注意我們對於寬度的保存,是有一個最左可拓展和最右可拓展矩陣,裡面放的是座標不是距離,所以找最小寬度,就是找最大的向左座標,最小的向右座標。

代碼流程

初始化

讀入數據,最左和最右一開始都是自己,最上就是1

#include <iostream>
#include <cstring>
using namespace std;
int n, m, ans =0;
int a[2010][2010], lft[2010][2010], rght[2010][2010], up[2010][2010];
int main(){
    memset(a, 0, sizeof(a));
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            char ch;
            cin >> ch;
            if(ch == 'F')a[i][j] = 1;
            lft[i][j] = j;
            rght[i][j] = j;
            up[i][j] = 1;
        }
    }
}

初始化最左

利用遞推思想,lft[i][j] 為當前坐標可達到的最左座標。若當前位置非0,就是可走,否則不可以走,如果旁邊是邊界,不可以走,則有if(a[i][j] && a[i][j - 1]) lft[i][j] = lft[i][j - 1];

for(int i = 1; i <= n; i++){
    for(int j = 1; j <= m; j++){
        if(a[i][j] && a[i][j - 1]){
            lft[i][j] = lft[i][j - 1];
        }
    }
}

初始化最右

利用遞推思想,rght[i][j]為當前坐標可到達最右的座標。思想跟初始化最左相似,不解釋了,可以注意的是我們的內置循環為倒循環,因為從右走到左過來。

for(int i = 1; i <= n; i++){
    for(int j = m; j >= 1; j++){
        if(a[i][j] && a[i][j + 1])rght[i][j] = rght[i][j + 1];
    }
}

Core Part

我們從(1,1)出發,每走完一層更新up的數據,因為第一層最高的up就是1,我們接下來只需要分析上一層的同一列是否為障礙物就可以了,並且同時間我們也找出最大寬度,結算一次。

for(int i = 1; i <= n; i++){
    for(int j = 1; j <= m; j++){
        if(a[i][j] && a[i - 1][j]){
            lft[i][j] = max(lft[i][j], lft[i - 1][j]);
            rght[i][j] = min(rght[i][j], rght[i - 1][j]);
            up[i][j] = up[i - 1][j] + 1;
        }
        if(!a[i][j])continue;
        int x = rght[i][j] - lft[i][j];
        ans = max(ans, up[i][j] * x);
    }
}

完整代碼

#include <iostream>
using namespace std;
int n, m;
int a[2010][2010], lft[2010][2010], rght[2010][2010], ans = 0, up[2010][2010];
int main(){
    cin >> n >> m;
    for(int i  = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            char ch;
            cin >> ch;
            if(ch == 'F')a[i][j] = 1;
            lft[i][j] = j;
            rght[i][j] = j;
            up[i][j] = 1;
        }
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            if(a[i][j] && a[i][j - 1])lft[i][j] = lft[i][j - 1];
        }
    }
    for(int i = 1; i <= n; i++){
        for(int j = m; j >= 1; j--){
            if(a[i][j] && a[i][j + 1])rght[i][j] = rght[i][j + 1];
        }
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            if(a[i][j] && a[i - 1][j]){
                lft[i][j] = max(lft[i - 1][j], lft[i][j]);
                rght[i][j] = min(rght[i - 1][j], rght[i][j]);
                up[i][j] = up[i - 1][j] + 1;
            }
            if(!a[i][j])continue;
            int x = rght[i][j] - lft[i][j] + 1;
            ans = max(ans, x * up[i][j]);
        }
    }
    cout << ans * 3;
    return 0;
}

Monotonic Stack

積水面積複雜版

只要得到每一個格子的可向上拓展高度就可以得到把每一行當成積水面積差不多的題目來做了。

只不過我們要讓棧內的元素保持升序,假設我們棧裡的元素是升序並且有k個元素,如果第k+1元素進來,他小於棧頂元素,也就是說前k個元素中那些比他大的元素都是不可能成為這個矩形的高度的,但同時這些過高的矩形本身有可能就是最大面積,所以我們把他們彈出來進行結算,如此類推。我們對每一行都做一樣的操作,取得最優值就可以了。

但是有機會遇到一樣高的元素,那麼我們就開結構體,把這一個元素的寬加1,就可以了。

就像下面的一樣如果左邊是題目給的資料,有邊是我們放進棧的情況。

最後我們做總結就可以了。

如何做總結呢?

先把棧頂的矩形拿出來,算一下他的面積,再把他彈出去,把他的寬度繼承給下一個棧頂元素,如此類推,我們可以拿到所有棧內元素,以他們為高度的所有可能最大面積了。

 代碼

初始化

先讀入數據

#include <iostream>
#include <vector>
using namespace std;
int n, m;
int f[2050][2050];
char c;
struct node{
    int val, len;
}a[2050];
vector<node> s;
int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            cin >> c;
            if(c == 'F') f[i][j] = f[i - 1][j] + 1;
        }
    }
    return 0;
}

Core Part

由於每一個位置可向上拓展的地方都記錄下來了,所以我們可以直接枚舉每一行進行總結。

每次彈棧後,把該矩形的寬度繼承下去,並且彈完後,把所有寬度賦值給新進來的數值。

void check(int r){
    memset(a, 0, sizeof(a));
    a[1].val = f[x][1];
    a[1].len = 1;
    while(!s.empty())s.pop_back();
    for(int i = 1; i <= m; i++){
        int width = 0;
        while(!s.empty() && f[x][i] <= s.back().val){
            width += s.back().len;
            maxArea = max(maxArea, width * s.back().val);
            s.pop_back();
        }
        a[i].val = f[x][i];
        a[i].len = width + 1;
        s.push_back(a[i]);
    }
    int w = 0;
    while(!s.empty()){
        w+=s.back().len;
        maxArea = max(maxArea, width * s.back().val);
        s.pop_back();
    }
}

完整代碼

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
int n, m, maxArea;
int f[2050][2050];
char c;
struct node{
    int val, len;
}a[2050];
vector<node> s;
void check(int r){
    memset(a, 0, sizeof(a));
    a[1].val = f[r][1];
    a[1].len = 1;
    while(!s.empty())s.pop_back();
    for(int i = 1; i <= m; i++){
        int width = 0;
        while(!s.empty() && f[r][i] <= s.back().val){
            width += s.back().len;
            maxArea = max(maxArea, width * s.back().val);
            s.pop_back();
        }
        a[i].val = f[r][i];
        a[i].len = width + 1;
        s.push_back(a[i]);
    }
    int width = 0;
    while(!s.empty()){
        width +=s.back().len;
        maxArea = max(maxArea, width * s.back().val);
        s.pop_back();
    }
}
int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            cin >> c;
            if(c == 'F') f[i][j] = f[i - 1][j] + 1;
        }
    }
    for(int i = 1; i<= n; i++)check(i);
    cout << maxArea*3 << endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值