C++で解く!2つの配列から選ぶ部分配列の最大OR和の求め方
問題文
正の整数からなる2つの配列が与えられます。それぞれの配列から同じサイズの部分配列を1つずつ選び、その2つの部分配列に対してビットごとの論理和(OR)を計算したとき、その合計として考えられる最大値を求めてください。
例
たとえば、次の2つの配列が与えられたとします。
arr1[] = {1, 2, 4, 3, 2}
arr2[] = {1, 3, 3, 12, 2}
この場合、以下のように部分配列を選ぶと最大の結果が得られます。
Subarr1[] = {2, 4, 3}
Subarr2[] = {3, 3, 12}
このとき Subarr1 のOR値は 7、Subarr2 のOR値は 15 となり、合計は 22 になります。
アプローチ:ORの単調性を利用する
この問題を効率的に解く鍵は、OR演算の重要な性質にあります。ORでは、すでに立っているビットが要素を追加しても消えることはなく、新しいビットが立ちうるだけです。つまり、部分配列に含める要素を増やすほど、ORの値は増加するか、せいぜい変わらないという単調性が成り立ちます。
したがって、ある配列から得られるOR値の最大は、その配列全体のORを取ったときに実現されます。本問題では2つの配列の長さがともにnであるため、それぞれの配列全体を選べば「同じサイズの部分配列」という条件も同時に満たします。
結論として、答えは次の式で求められます。
f(a, 1, n) + f(b, 1, n)
ここで f(x, l, r) は、配列 x の区間 [l, r] に含まれる要素すべてのOR値を表します。
C++による実装例
#include <bits/stdc++.h>
using namespace std;
int getMaximumSum(int *arr1, int *arr2, int n) {
int sum1 = 0;
int sum2 = 0;
for (int i = 0; i < n; ++i) {
sum1 = sum1 | arr1[i];
sum2 = sum2 | arr2[i];
}
return sum1 + sum2;
}
int main() {
int arr1[] = {1, 2, 4, 3, 2};
int arr2[] = {1, 3, 3, 12, 2};
int n = sizeof(arr1) / sizeof(arr1[0]);
cout << "Maximum result = " << getMaximumSum(arr1, arr2, n) << endl;
return 0;
}実行結果
上記のプログラムをコンパイルして実行すると、次の出力が得られます。
Maximum result = 22
計算量
各配列を一度だけ走査すればよいため、時間計算量は O(n)、使用する追加メモリは定数のみで空間計算量は O(1) です。部分配列の組み合わせを全探索する非効率な手法と比べて、非常にシンプルかつ高速な解法といえます。
-
C++でmを法とする最大部分配列の総和を求めるアルゴリズム
C++でmを法とする最大部分配列の総和とは この問題では、サイズnの整数型配列と整数mが与えられます。求めるのは、すべての部分配列の総和の中から、mで割った余りが最大となる値です。 問題の概要 − 各部分配列の全要素の合計をmで割った余りを計算し、その中で最大の値を求めます。 具体例で問題を確認しよう まずは例を見て、問題の内容を理解しましょう。 入力 − array = {4, 9, 2}、m = 6 出力 − 5 説明 − すべての部分配列と、それぞれを6で割った余りは以下の通りです。 {4}: 4 % 6 = 4 {9}: 9 % 6 = 3 {2}: 2 % 6 = 2 {4, 9}
-
C++で配列の最大平衡和(イクリブリアム・サム)を求める方法
問題概要配列 arr[] が与えられたとき、あるインデックス i における「接頭辞和(プレフィックスサム)」と「接尾辞和(サフィックスサム)」が一致する値の中から、最大値を見つけるのがこの問題の目的です。この一致する値は「平衡和(イクリブリアム・サム)」と呼ばれます。例入力配列が以下の場合を考えてみましょう。Arr[] = {1, 2, 3, 5, 3, 2, 1}このとき出力は 11 になります。その理由は次の通りです。接頭辞和 = arr[0..3] = 1 + 2 + 3 + 5 = 11接尾辞和 = arr[3..6] = 5 + 3 + 2 + 1 = 11インデックス 3 を境にし