C++で1のみを含む最大サイズの長方形部分行列を見つける方法
このチュートリアルでは、すべての要素が1である最大サイズの長方形部分行列(バイナリサブマトリックス)を見つけるプログラムについて解説します。
0と1のみで構成された2次元行列が与えられ、その中から「1だけで構成される最も大きな部分行列」の面積を求めるのが課題です。
アルゴリズムの考え方
この問題は、「ヒストグラムの中で最大の長方形を求める」という古典的な問題を応用することで効率的に解くことができます。手順は以下の通りです。
- 1行目をそのままヒストグラムとみなし、スタックを使って最大長方形の面積を計算します。
- 2行目以降では、現在のセルが1であれば、直前の行の同じ列の値に加算します。これにより、各列について「上方向へ連続して1が続いている高さ」が求まります。
- 更新後の各行に対して同様に最大長方形の面積を計算し、これまでの最大値より大きければ答えを更新します。
この手法の計算量はO(R×C)であり、すべての長方形候補を総当たりで調べる方法に比べて大幅に高速です。
サンプルコード
#include <bits/stdc++.h>
using namespace std;
#define R 4
#define C 4
// ヒストグラム内の最大長方形の面積を求める
int maxHist(int row[]) {
stack<int> result;
int top_val;
int max_area = 0;
int area = 0;
int i = 0;
while (i < C) {
if (result.empty() || row[result.top()] <= row[i])
result.push(i++);
else {
top_val = row[result.top()];
result.pop();
area = top_val * i;
if (!result.empty())
area = top_val * (i - result.top() - 1);
max_area = max(area, max_area);
}
}
while (!result.empty()) {
top_val = row[result.top()];
result.pop();
area = top_val * i;
if (!result.empty())
area = top_val * (i - result.top() - 1);
max_area = max(area, max_area);
}
return max_area;
}
// 最大の部分行列の面積を返す
int maxRectangle(int A[][C]) {
int result = maxHist(A[0]);
for (int i = 1; i < R; i++) {
for (int j = 0; j < C; j++)
if (A[i][j])
A[i][j] += A[i - 1][j];
result = max(result, maxHist(A[i]));
}
return result;
}
int main() {
int A[][C] = {
{ 0, 1, 1, 0 },{ 1, 1, 1, 1 },{ 1, 1, 1, 1 },{ 1, 1, 0, 0 },
};
cout << "Area of maximum rectangle is " <<
maxRectangle(A);
return 0;
}
実行結果
Area of maximum rectangle is 8
この入力例では、2行目と3行目にまたがる「縦2×横4」の長方形が最大となり、その面積は8になります。
-
C++で指定した合計値になる最大サイズの部分集合を求める方法
問題文 N個の要素からなる配列と合計値が与えられたとき、要素の合計が指定された値と一致する「最大サイズの部分集合」のサイズを求める問題です。 例 入力配列が arr = { 2, 3, 5, 10 }、合計値が sum = 20 の場合、出力は 4 になります。 なぜなら、 2 + 3 + 5 + 10 = 20 となり、配列の全要素を選んだ部分集合の合計が指定された合計値と一致するためです。 アルゴリズム この問題は動的計画法(DP)を用いて効率的に解くことができます。 まず、通常の部分和問題と同様に subset[i][j] というブール型のDPテーブルを用意します。これは「最初の j
-
C++で合計が0となるすべての部分配列を出力する方法
この記事では、整数値の配列が与えられたときに、要素の合計が0になるすべての部分配列(連続した要素の並び)を見つけ出し、それらを出力する方法をC++で解説します。 問題の概要 まず、具体例を使って問題を理解しましょう。 入力: arr[] = {-5, 0, 2, 3, -3, 4, -1} この配列の場合、合計が0になる部分配列は以下の通りです。 {0} … インデックス1のみ {-5, 0, 2, 3} … インデックス0〜3 {3, -3} … インデックス3〜4 {-3, 4, -1} … インデックス4〜6 {-5, 0, 2, 3, -3, 4, -1} … インデックス0〜6(配