C++で配列の全サブセットのAND値の最小値を求める方法
問題の概要
整数の配列が与えられたとき、その配列から取り出せるすべてのサブセット(部分集合)について要素全体のAND(ビットごとの論理積)を計算し、その中で最小となるAND値を求めて出力するのが課題です。
例
配列 arr[] = {1, 2, 3, 4, 5} の場合、2要素からなるサブセットのAND値は次のようになります。
(1 & 2) = 0 (1 & 3) = 1 (1 & 4) = 0 (1 & 5) = 1 (2 & 3) = 2 (2 & 4) = 0 (2 & 5) = 0 (3 & 4) = 0 (3 & 5) = 1 (4 & 5) = 4
解法のポイント
すべてのサブセットを列挙してAND値を計算するのは非常に非効率です。ここでAND演算の重要な性質に注目しましょう。
- AND演算では、ビットが「1」から「0」に変わることはあっても、「0」から「1」に変わることはありません。
- つまり、サブセットに要素を追加するたびに、AND値は減るか同じ値のままとなり、決して増えることはありません。
- したがって、配列の全要素を含むサブセット(配列全体)のAND値こそが、すべてのサブセットの中で最小のAND値になります。
この性質より、答えは単純に「配列の全要素のAND」となり、配列を一度走査するだけで求められます。
C++での実装例
#include <bits/stdc++.h>
using namespace std;
int getMinAndValue(int *arr, int n) {
int result = arr[0];
for (int i = 1; i < n; ++i) {
result = result & arr[i];
}
return result;
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
int n = sizeof(arr) / sizeof(arr[0]);
cout << "Minimum value = " << getMinAndValue(arr, n) << endl;
return 0;
}上記のプログラムをコンパイルして実行すると、次の出力が得られます。
出力
Minimum value = 0
計算量
- 時間計算量: O(n) — 配列の各要素を1回ずつ処理するだけです。
- 空間計算量: O(1) — 追加のメモリは不要です。
まとめ
AND演算の単調性(要素を増やしてもAND値が増えることはない性質)を利用することで、サブセットを全列挙することなく、配列全体のAND値を1回の走査で計算するだけで最小値が求められることが分かりました。シンプルでありながら、ビット演算の性質を活かした効率的な解法と言えます。
-
C++で配列内の各要素に最も近い大きい値を効率的に検索する方法
この記事では、配列内の各要素に対して「最も近い大きい値」を効率的に検索する方法を解説します。ある要素 x より大きい値が配列内に存在する場合、その中で最も小さい値(次に大きい要素)をその要素の答えとし、存在しない場合は -1 を出力します。例として、配列が {10, 5, 11, 10, 20, 12} の場合、結果は {11, 10, 12, 11, -1, 20} となります。最大値の 20 より大きい要素は配列内に存在しないため、20 に対しては -1 が出力されます。解決のアプローチこの問題は C++ STL の set(セット)を使うと簡単に解決できます。set は二分探索木をベース
-
C++で配列の全要素を同じ値にするための最小削除操作数を求めるアルゴリズム
問題概要n個の要素からなる配列が与えられます。要素には重複が含まれる場合があります。この配列から任意の数の要素を削除できるとき、すべての要素を同じ値にするために必要な最小の削除数を求めるのが課題です。例として、次の配列を考えてみましょう。arr[] = {10, 8, 10, 7, 10, -1, -4, 12}この場合、最も多く出現している「10」以外の5つの要素を削除すれば、配列の全要素を10で統一できます。つまり、答えは5回の削除となります。解法の考え方この問題の鍵となるのは、「削除する量を最小化する = 残す要素の数を最大化する」という発想です。全要素を同じ値にするためには、必ずどれか