C++で部分配列を1回だけ反転して0の個数を最大化する方法
問題文
0と1のみから構成される2値配列(バイナリ配列)が与えられます。ここで、部分配列を1回だけ反転できるという条件のもとで、配列全体に含まれる0の個数を最大化することを考えます。
なお、反転操作とは、指定した範囲内のすべての0を1に、1を0に切り替える処理のことです。
具体例
次の配列を考えてみましょう。
arr = {1, 1, 0, 0, 0, 0, 0}先頭の2つの「1」を「0」に反転すると、配列全体が次のようになります。
{0, 0, 0, 0, 0, 0, 0}この場合、0の個数は7となり、これが最大値です。
解法のポイント
ある部分配列を反転すると、その範囲内の1は0に、0は1に変わります。したがって、反転後の0の総数は次の式で表せます。
最終的な0の個数 = 元の配列の0の個数 + (部分配列内の1の個数 − 部分配列内の0の個数)
つまり、「1の個数 − 0の個数」が最大となる部分配列を見つければよいことになります。
アルゴリズム
- すべての部分配列を調べ、「1の個数 − 0の個数」の値が最大になる部分配列を探します。
- この最大値を
maxDiffとします。 - 最終的な答えとして、元の配列の0の個数 + maxDiff を返します。
この手法の計算量は、全ての開始位置・終了位置の組み合わせを調べるため O(n²) となります。
C++での実装例
#include <bits/stdc++.h>
using namespace std;
int getMaxSubArray(int *arr, int n){
int maxDiff = 0;
int zeroCnt = 0;
for (int i = 0; i < n; ++i) {
if (arr[i] == 0) {
++zeroCnt;
}
int cnt0 = 0;
int cnt1 = 0;
for (int j = i; j < n; ++j) {
if (arr[j] == 1) {
++cnt1;
}
else {
++cnt0;
}
maxDiff = max(maxDiff, cnt1 - cnt0);
}
}
return zeroCnt + maxDiff;
}
int main(){
int arr[] = {1, 1, 0, 0, 0, 0, 0};
int n = sizeof(arr) / sizeof(arr[0]);
cout << "Maximum subarray size = " << getMaxSubArray(arr, n) << endl;
return 0;
}実行結果
上記のプログラムをコンパイルして実行すると、次の出力が得られます。
Maximum subarray size = 7
まとめ
本記事では、2値配列に対して部分配列を1回だけ反転できる場合に、0の個数を最大化する方法を紹介しました。鍵となるのは「反転によって増える0の数=部分配列内の(1の個数 − 0の個数)」という着眼点です。全部分配列を走査するO(n²)のシンプルな実装で正しく解くことができます。
-
C++で可変数の引数(可変長引数)を扱う方法
プログラミングをしていると、引数の個数があらかじめ決まっていない関数、つまり呼び出しのたびに異なる数のパラメータを受け取れる関数が必要になる場面があります。C/C++ではこのような状況に対応する仕組みが用意されており、要件に応じて可変個の引数を受け取る関数を自由に定義できます。以下に、そのような関数の定義例を示します。 int func(int, ... ) { . . . } int main() { func(1, 2, 3); func(1, 2, 3, 4); } 注目すべきは、関数func()の最後の引数が省略記号(ピリオド3つの「...」)になってい
-
C++のCHAR_BITとは?意味と使い方を解説
CHAR_BITは、char型が持つビット数を表すマクロです。C++では「limits.h」ヘッダーファイル(C++では<climits>)で宣言されており、一般的な環境では1バイトが8ビットであることを示します。このマクロを利用することで、移植性の高いコードを書くことができます。環境に依存せずにchar型のビット数を取得できるため、ビット演算やデータサイズの計算に役立ちます。CHAR_BITの使用例以下は、C++でCHAR_BITを使用したサンプルコードです。CHAR_BITとsizeofを組み合わせてint型の全ビット数を求め、整数値を2進数形式で出力しています。#includ