C++で配列の全要素にXOR演算を適用して合計を最小化する方法
問題の説明
サイズNの配列が与えられます。配列の各要素とある整数XとのXOR演算を行ったとき、その結果の合計が最小となるようなXを見つけてください。
例として、入力配列が arr[] = {8, 5, 7, 6, 9} の場合、最小合計は 30 になります。
各配列要素の2進数表現は次のとおりです。
8 : 1000 5 : 0101 7 : 0111 6 : 0110 9 : 1001
X = 5 のとき、XOR演算後の各値と合計は以下のようになります。
8 ^ 5 = 13 5 ^ 5 = 0 7 ^ 5 = 2 6 ^ 5 = 3 9 ^ 5 = 12 合計 = 30(13 + 0 + 2 + 3 + 12)
アルゴリズム
- 32ビット整数を扱うため、サイズ32のビットマップ(bitMap)を作成します。
- 配列を走査し、各要素について以下を処理します。
a. 要素の0番目のビットが立っている場合、bitMap[0]のカウントをインクリメントします。
b. 1番目のビットが立っている場合、bitMap[1]のカウントをインクリメントします。以降のビットも同様に処理します。 - bitMap配列を走査してXを求めます。
if bitMap[i] > n/2 ならば、X = X + pow(2, i) - 入力配列を走査し、各要素とXのXOR演算を行います。
- 演算結果の合計を計算して返します。
なぜこの方法が機能するのか
XOR演算では、Xの特定のビットが1である位置に対応する配列要素のビットが反転します(0は1に、1は0に変わります)。あるビット位置が配列の半分を超える要素で1になっている場合、Xのそのビットを1に設定すると、「1→0」に変わる要素の数が「0→1」に変わる要素の数を上回るため、合計を減らすことができます。逆に、そのビットが半分以下の要素にしか立っていなければ、Xのビットは0のままにします。この処理を各ビット位置について行うことで、合計が最小になるXが得られます。
実装例(C++)
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
#define SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
const int MAX_SIZE = 32;
int getSum(int *arr, int n){
int bitMap[MAX_SIZE];
int bitLength = 0;
int sum = 0;
fill(bitMap, bitMap + MAX_SIZE, 0);
for (int i = 0; i < n; ++i) {
int num = arr[i];
int f = 0;
while (num > 0) {
int rem = num % 2;
num = num / 2;
if (rem == 1) {
bitMap[f]++;
}
++f;
bitLength = max(bitLength, f);
}
}
int candidate = 0;
for (int i = 0; i < bitLength; ++i) {
int num = pow(2, i);
if (bitMap[i] > n / 2) {
candidate += num;
}
}
for (int i = 0; i < n; ++i) {
sum += arr[i] ^ candidate;
}
return sum;
}
int main(){
int arr[] = {8, 5, 7, 6, 9};
cout << "Minimum sum: " << getSum(arr, SIZE(arr)) << "\n";
return 0;
}実行結果
上記のプログラムをコンパイルして実行すると、次の出力が得られます。
Minimum sum: 30
-
C++で無向グラフの連結成分ごとの最小要素の合計を求める方法
この記事では、無向グラフのすべての連結成分に含まれる最小要素の合計を求める問題を、C++を使って解く方法を解説します。 問題の設定は次のとおりです。N個の整数からなる配列 arr が与えられ、arr[i] は (i+1) 番目のノードの値を表します。また、M個の辺のペア (u, v) が与えられ、それぞれノード u とノード v が辺で結ばれていることを示します。このとき、無向グラフの各連結成分ごとに最小値を求め、それらをすべて合計した値を出力するプログラムを作成します。なお、他のどのノードともつながっていないノードは、それ単独で1つの連結成分として扱います。 問題例 具体的な入力例で問題を確
-
C++で配列の全要素を削除するために必要な最小操作数を求める方法
問題の概要整数型の配列 arr が与えられたとき、配列のすべての要素を削除するために必要な最小の操作数を求めるのが課題です。ただし、要素を削除する際には次の制約が課されます。配列から任意の要素を自由に選択でき、その要素で割り切れるすべての要素を一度に配列から削除できる。例えば、arr[] = {2, 4, 15, 10, 8, 5, 3} の場合、すべての要素を削除するには3回の操作が必要です。2 を選択すると、{2, 4, 10, 8} が削除されます。5 を選択すると、{5, 15} が削除されます。3 を選択すると、{3} が削除されます。アルゴリズム配列を昇順にソートし、各要素の出現回