C++
 Computer >> コンピューター >  >> プログラミング >> C++

【C++】配列のビットごとのORを最大化するアルゴリズム


問題の概要

N個の整数からなる配列が与えられます。ここで、配列内の任意の1つの要素に対して、指定された整数 x最大 k 回まで乗算するという操作を一度だけ行い、配列全体のビットごとのOR(論理和)を最大化することを考えます。

たとえば、入力配列が {4, 3, 6, 1}k = 2x = 3 の場合、得られる最大値は 55 となります。これは、要素「6」に 3^2 = 9 を掛けて 54 とし、残りの要素 {4, 3, 1} とのORを取ると 54 | 4 | 3 | 1 = 55 になるためです。

アルゴリズム

どの要素を何倍すればよいかを毎回総当たりで調べるのは非効率です。そこで、前方向と後ろ方向のORを事前に計算しておくことで、効率的に解くことができます。

  1. 各要素に x^k(xのk乗)を乗算し、それよりにあるすべての要素のビットごとのORと組み合わせます(prefixOR)
  2. 同様に、それよりにあるすべての要素のビットごとのORとも組み合わせます(suffixOR)
  3. すべての要素について上記の計算を行い、得られた値の最大値を返します

この手法により、時間計算量は O(N + K)、空間計算量は O(N) で解くことができます。

実装例(C++)

#include <bits/stdc++.h>
using namespace std;
int getMaxOr(int *arr, int n, int k, int x){
    int prefixSum[n + 1];
    int suffixSum[n + 1];
    int power = 1;
    for (int i = 0; i < k; ++i) {
        power = power * x;
    }
    prefixSum[0] = 0;
    for (int i = 0; i < n; ++i) {
        prefixSum[i + 1] = prefixSum[i] | arr[i];
    }
    suffixSum[n] = 0;
    for (int i = n - 1; i >= 0; --i) {
        suffixSum[i] = suffixSum[i + 1] | arr[i];
    }
    int result = INT_MIN;
    for (int i = 0; i < n; ++i) {
        result = max(result, prefixSum[i] | (arr[i] * power) | suffixSum[i + 1]);
    }
    return result;
}
int main(){
    int arr[] = {4, 3, 6, 1};
    int n = sizeof(arr) / sizeof(arr[0]);
    int k = 2;
    int x = 3;
    cout << "Result = " << getMaxOr(arr, n, k, x) << endl;
    return 0;
}

実行結果

上記のプログラムをコンパイルして実行すると、次の出力が得られます。

Result = 55

コードのポイント解説

  • 変数 power には x^k の値が格納されます。k回の乗算をあらかじめ1回の計算にまとめておくことで無駄を省いています。
  • prefixSum[i] は「位置 i の直前までの要素のOR」、suffixSum[i + 1] は「位置 i より後ろの要素のOR」をそれぞれ保持しています。
  • ループ内では、各要素 arr[i]power 倍した場合の配列全体のORを計算し、これまでの最大値と比較しながら更新していきます。
  • どの要素を強化しても他の要素は元のままなので、prefixOR と suffixOR を分けて管理するだけで全パターンを高速に評価できます。
  1. C++で配列を逆順に反転する方法を解説

    本記事では、C++を使って配列を逆順(降順)に反転する方法を解説します。ループで配列を走査しながら、最も大きいインデックスの要素と最も小さいインデックスの要素を順次入れ替えていくことで、配列全体を反転させます。 アルゴリズムの考え方 配列の反転は、以下の手順で実現できます。 先頭を指す low ポインタと、末尾を指す high ポインタを用意します。 low < high が成り立つ間、swap 関数を使って両端の要素を入れ替えます。 1回の入れ替えごとに low を1つ進め、high を1つ戻し、中央に向かって処理を進めます。 この方法なら、計算量は O(n)、追加のメモリは不要(

  2. C++で配列内の数値の頻度(出現回数)を求める方法

    配列に n 個の異なる要素が格納されているとします。この配列の中から、特定の要素が何回出現するか(頻度)を調べたい場合があります。例えば、配列 A = [5, 12, 26, 5, 3, 4, 15, 5, 8, 4] の中で「5」の頻度を調べると、答えは 3 になります。アルゴリズムの考え方この問題は、次の手順で解くことができます。1. 配列を左端から順に走査します。2. 現在の要素が調べたい数値と一致したら、カウンターを1つ増やします。3. 一致しない場合は、そのまま次の要素へ進みます。4. 配列の最後まで走査したら、カウンターの値が頻度となります。このアルゴリズムの計算量は O(n) で