C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で2次元バイナリ行列から最大の長方形を求める方法(スタック活用)

問題概要

0と1から構成される2次元のバイナリ行列が与えられます。この行列の中から「1」だけで構成される最大の長方形を見つけ、その面積を返すのが本記事のテーマです。

この問題を解くには、以下の手順に従います。

getAns関数(ヒストグラム内の最大長方形を求める)

  • 配列aを受け取る関数getAnsを定義します。
  • スタックstを作成し、i := 0、ans := 0 と初期化します。
  • iがaのサイズ未満である間、以下を繰り返します。
    • スタックが空、または a[i] がスタックトップの値以上の場合:iをstに挿入し、iを1増やします。
    • それ以外の場合:
      • height := a[スタックトップ] とし、スタックから削除(pop)します。
      • width := スタックが空なら i、それ以外は i − スタックトップ − 1 とします。
      • area := height × width とします。
      • ans := max(ans, area) と更新します。
  • スタックが空になるまで、同じ要領で処理を続けます。
    • height := a[スタックトップ] とし、スタックから削除します。
    • width := stが空ならaのサイズ、それ以外は aのサイズ − スタックトップ − 1 とします。
    • area := height × width とします。
    • ans := max(ans, area) と更新します。
  • ansを返します。

mainメソッドでの処理

  • ans := 0、n := xの行数 とします。
  • nが0なら0を返します。
  • m := x[0]の列数 とします。
  • サイズmの配列heightを作成します。
  • iを0からn−1まで繰り返します。
    • jを0からm−1まで繰り返します。
      • x[i][j] が '1' なら height[j] を1増やし、そうでなければ height[j] := 0 とします。
    • ans := max(ans, getAns(height)) と更新します。
  • ansを返します。

アルゴリズムのポイント

各行を走査するたびに、その時点での各列の「連続する1の高さ」をheight配列に記録しています。これは、ある行までを見たときのヒストグラムとみなすことができます。ヒストグラム内で作れる最大の長方形は、単調増加スタックを用いることで効率的に求められます。

スタックにはインデックスを格納し、対応する高さが常に昇順になるよう管理します。現在の高さがスタックトップより小さい場合、それより高い柱ではこれ以上長方形を伸ばせないため、その時点で面積を確定させてpopします。幅は「現在位置i」と「pop後のスタックトップ」の間隔から算出されます。

計算量

時間計算量は O(n × m)、空間計算量は O(m) となります(nは行数、mは列数)。すべての長方形候補を総当たりする方法(O(n²m²))と比べ、大幅に高速なのが特徴です。

C++での実装例

理解を深めるために、以下の実装を見てみましょう。

#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
    int getAns(vector<int> a){
        stack<int> st;
        int i = 0;
        int ans = 0;
        while(i < a.size()){
            if(st.empty() || a[i] >= a[st.top()]){
                st.push(i);
                i++;
            } else {
                int height = a[st.top()];
                st.pop();
                int width = st.empty() ? i : i - st.top() - 1;
                int area = height * width;
                ans = max(ans, area);
            }
        }
        while(!st.empty()){
            int height = a[st.top()];
            st.pop();
            int width = st.empty() ? a.size() : a.size() - st.top() - 1;
            int area = height * width;
            ans = max(ans, area);
        }
        return ans;
    }
    int maximalRectangle(vector<vector<char>>& x) {
        int ans = 0;
        int n = x.size();
        if(!n) return 0;
        int m = x[0].size();
        vector<int> height(m);
        for(int i = 0; i < n; i++){
            for(int j = 0; j < m; j++){
                if(x[i][j] == '1') height[j]++;
                else height[j] = 0;
            }
            ans = max(ans, getAns(height));
        }
        return ans;
    }
};

int main(){
    vector<vector<char>> v = {
        {'1','0','1','0','0'},
        {'1','0','1','1','1'},
        {'1','1','1','1','1'},
        {'1','0','0','1','0'}
    };
    Solution ob;
    cout << ob.maximalRectangle(v);
    return 0;
}

入力

{{'1','0','1','0','0'},
{'1','0','1','1','1'},
{'1','1','1','1','1'},
{'1','0','0','1','0'}
}

出力

6

この入力では、2行目と3行目の2〜4列目にわたる 2 × 3 = 6 マスの長方形が最大となるため、出力は 6 になります。

  1. C++で解く長方形エリアII ― 座標圧縮と走査線法による被覆面積の計算

    問題概要 軸に平行な長方形のリストが与えられるものとします。各 rectangle[i] = {x1, y1, x2, y2} において、(x1, y1) は i 番目の長方形の左下隅の座標、(x2, y2) は右上隅の座標を表します。 求めたいのは、平面上でこれらすべての長方形が覆っている領域の合計面積です。答えは非常に大きな値になる可能性があるため、109 + 7 で割った余りを返すことになっています。 たとえば、入力が次のような場合を考えてみましょう。 このとき、出力は 6 となります。 解法の方針:座標圧縮+走査線(スイープライン) この問題は、座標圧縮(座標の離散化)と走査線法(

  2. C++で2つの長方形が覆う合計面積を求めるアルゴリズム

    2次元平面上に置かれた2つの軸に平行な長方形について、それらが覆う領域の合計面積を求める問題を考えます。各長方形は、左下の頂点と右上の頂点の座標によって定義されます。下図のように、第1の長方形は左下 (A, B)・右上 (C, D)、第2の長方形は左下 (E, F)・右上 (G, H) として表されます。解き方のアプローチこの問題を解くための手順は以下の通りです。まず、2つの長方形が重なっているかどうかを判定します。C ≤ E、A ≥ G、B ≥ H、D ≤ F のいずれかが成り立つ場合、2つの長方形は重ならないため、それぞれの面積の和 (C − A) × (D − B) + (G − E)