C++でバイナリ行列をすべて1にするための最小操作回数を求めるアルゴリズム
問題概要
N行M列のバイナリ行列(各要素が0または1の行列)が与えられます。この行列に対して許可されている操作は、任意のインデックス (x, y) を選択し、左上を (0, 0)、右下を (x-1, y-1) とする長方形領域内のすべての要素を反転(トグル)することです。ここで「反転」とは、1を0に、0を1に変更することを意味します。
この問題の目的は、行列のすべての要素を1にするために必要な最小操作回数を求めることです。
具体例
入力行列:
{0, 0, 0, 1, 1}
{0, 0, 0, 1, 1}
{0, 0, 0, 1, 1}
{1, 1, 1, 1, 1}
{1, 1, 1, 1, 1}
答え:1この場合、一度だけ操作を行えば十分です。(3, 3) を選択して左上の3×3の領域を反転させると、行列全体がすべて1になります。
アルゴリズムの考え方
この問題を解く鍵となるアイデアは、行列の右下端 (N-1, M-1) からスタートし、逆順に走査することです。
走査の過程で値が0のセルに出会ったら、そのセルを含む左上方向の長方形領域全体を反転します。右下から処理を進めることで、すでに確定した領域を壊さずに済むため、これが最適な戦略となります。
C++での実装例
#include <iostream>
#define ROWS 5
#define COLS 5
using namespace std;
int getMinOperations(bool arr[ROWS][COLS]) {
int ans = 0;
for (int i = ROWS - 1; i >= 0; i--){
for (int j = COLS - 1; j >= 0; j--){
if(arr[i][j] == 0){
ans++;
for (int k = 0; k <= i; k++){
for (int h = 0; h <= j; h++){
if (arr[k][h] == 1)
arr[k][h] = 0;
else
arr[k][h] = 1;
}
}
}
}
}
return ans;
}
int main() {
bool mat[ROWS][COLS] = {
0, 0, 1, 1, 1,
0, 0, 0, 1, 1,
0, 0, 0, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1
};
cout << "Minimum required operations = " << getMinOperations(mat) << endl;
return 0;
}実行結果
上記のプログラムをコンパイルして実行すると、次の出力が得られます。
Minimum required operations = 3
計算量について
この実装では、0のセルを見つけるたびに最大 O(N×M) の反転処理を行うため、最悪計算量は O((N×M)²) となります。より大規模な行列に対しては、差分配列や二次元累積和を活用することで、O(N×M) まで高速化できる点にも注目です。
-
C++でバイナリ行列をゼロ行列に変換するための最小反転回数を求める方法
m × n のバイナリ行列(0 と 1 のみで構成された行列)mat が与えられます。1 ステップごとに、任意のセルを 1 つ選び、そのセルのビットと、存在する場合は上下左右 4 つの隣接セルのビットをすべて同時に反転することができます。mat をゼロ行列(全要素が 0 の行列)へ変換するために必要な最小ステップ数を求めてください。解が存在しない場合は -1 を返します。 たとえば、入力が [[0,0], [0,1]] の場合、変換の過程は次のようになります。 この場合、3 ステップが必要となるため、出力は 3 になります。 解き方のアプローチ:BFS(幅優先探索)とビットマスク この問
-
C++で2つの二分探索木の全要素を昇順リストとして取得する方法
問題の概要2つの二分探索木(BST:Binary Search Tree)が与えられたとき、両方の木に含まれるすべての要素を昇順に並べたリストを返すことを考えます。例えば、次のような2つの二分探索木があるとします。木1:[2,1,4]木2:[1,0,3]この場合、出力は [0,1,1,2,3,4] となります。重複する値(この例では「1」)もそのまま保持される点に注意してください。解決のためのアプローチこの問題は、各BSTに対して反復的な中順走査(inorder traversal)を行い、マージソートのように2つの走査結果を統合することで効率的に解けます。手順は以下の通りです。結果を格納する