C++で先頭に0、その後に1が来るようにバイナリ配列を分割するための最小トグル回数
問題文
0と1のみを含むn個の整数からなる配列が与えられます。この配列を「前半がすべて0、後半がすべて1」という形に分割するために必要な最小のトグル回数(0を1に、または1を0に切り替える操作)を求めてください。
例
例えば、arr[] = {1, 0, 0, 1, 1, 1, 0} の場合、必要なトグル回数は2回です。具体的には、先頭の「1」と末尾の「0」をそれぞれ切り替えます。
アルゴリズム
- 問題を注意深く観察すると、インデックス0からn-1の間に必ず境界点が存在し、その点より左側にはすべての0が、右側にはすべての1が配置されるべきであることが分かります。
- この規則に当てはまらない要素は切り替え(トグル)の対象となります。そこで、左から右へ向かって0の出現回数を累積的に数えていくという考え方を用います。
各位置iを境界としたとき、必要なトグル回数は次の2つの合計として計算できます。
左側のコスト: 境界より左にある1の個数 = i − zeroCnt[i]
右側のコスト: 境界より右にある0の個数 = zeroCnt[n] − zeroCnt[i]
すべての境界位置についてこの値を評価し、その最小値を答えとします。この手法により、計算量はO(n)となり、単純な総当たり的なO(n²)の解法よりも効率的に処理できます。
実装例
#include <bits/stdc++.h>
using namespace std;
int getMinToggles(int *arr, int n) {
int zeroCnt[n + 1] = {0};
for (int i = 1; i <= n; ++i) {
if (arr[i - 1] == 0) {
zeroCnt[i] = zeroCnt[i - 1] + 1;
} else {
zeroCnt[i] = zeroCnt[i - 1];
}
}
int result = n;
for (int i = 1; i <= n; ++i) {
result = min(result, i - zeroCnt[i] + zeroCnt[n] - zeroCnt[i]);
}
return result;
}
int main() {
int arr[] = {1, 0, 0, 1, 1, 1, 0};
int n = sizeof(arr) / sizeof(arr[0]);
cout << "Minimum toggles = " << getMinToggles(arr, n) << endl;
return 0;
}
出力
Minimum toggles = 2
上記のプログラムをコンパイルして実行すると、この結果が出力されます。このコードでは、zeroCnt配列を使って先頭からi番目までに含まれる0の個数を累積的に記録しています。その後、各位置を境界としたときの「左側に残る1の数」と「右側に現れる0の数」の合計(=必要なトグル回数)を求め、その最小値を返す仕組みです。
-
C++でノード値の合計が最小となる二分木のレベルを求めるプログラム
二分木(バイナリツリー)を考えます。根(ルート)のレベルを1とし、その子のレベルを2、さらにその下のレベルを3というように定義します。このとき、レベルXに存在するすべてのノードの値の合計が最小になるような、最も小さいレベルXを見つけるのが本記事の目的です。例として、次のような二分木を考えてみましょう。この場合、出力は 2 となります。なぜなら、レベル2のノードの値の合計は 4 + (-10) = -6 となり、これが全レベルの中で最小だからです。解法のアプローチこの問題は、幅優先探索(BFS)を使って各レベルごとにノードの値の合計を計算し、その中で最小となるレベルを記録していくことで解けます。
-
C++で二分木の最小深度を求める方法を解説
二分木が与えられたとき、その木の最小深度(minimum depth)を求めることを考えます。最小深度とは、根ノードから最も近い葉ノードまでの最短経路に含まれるノード数のことです。 例えば、次のような二分木が入力として与えられた場合を考えてみましょう。 この場合、出力は 2 になります。これは、根ノード 3 から葉ノード 9 までの経路が最短だからです。 解決のためのアプローチ この問題は、幅優先探索(BFS)を用いて各レベルを順番に調べることで効率的に解決できます。手順は以下の通りです。 ツリーノードを格納する配列 aa を定義し、その末尾に root を挿入します 別の配列 ak を