C++で配列のすべての部分集合の合計値を効率的に求める方法
n個の要素を持つ配列Aが与えられたとき、その配列のすべての部分集合の合計値の総和を求める問題を考えてみましょう。
例えば、配列が A = [5, 6, 8] の場合、各部分集合とその合計は以下のようになります。
| 部分集合 | 合計 |
|---|---|
| 5 | 5 |
| 6 | 6 |
| 8 | 8 |
| 5, 6 | 11 |
| 6, 8 | 14 |
| 5, 8 | 13 |
| 5, 6, 8 | 19 |
| 総和 | 76 |
考え方のポイント
n個の要素を持つ配列の場合、部分集合の数は(空集合を含めて)2n 個存在します。
ここで重要な性質に注目してみましょう。各要素は、ちょうど 2(n−1) 回だけ部分集合に出現するという点です。
この性質を利用すると、すべての部分集合の合計値の総和は次の式で一発に計算できます。
総和 = (配列の全要素の合計) × 2^(n-1)
つまり、部分集合を実際に列挙して足し合わせる必要はなく、O(n) の計算量で答えを求められるのです。
C++での実装例
#include<iostream>
#include<cmath>
using namespace std;
int totalSum(int arr[], int n) {
int res = 0;
// 配列の全要素の合計を求める
for (int i = 0; i < n; i++)
res += arr[i];
// 全要素の合計に 2^(n-1) を掛ける
return res * pow(2, n - 1);
}
int main() {
int arr[] = { 5, 6, 8 };
int n = sizeof(arr)/sizeof(arr[0]);
cout << "すべての部分集合の合計値の総和: " << totalSum(arr, n) << endl;
}実行結果
すべての部分集合の合計値の総和: 76
まとめ
この問題は、各要素が 2(n−1) 回出現するという数学的な性質を利用することで、部分集合を全列挙することなく効率的に解くことができます。計算量は O(n) であり、要素数が多い配列でも高速に動作します。なお、n が大きい場合はオーバーフローに注意し、必要に応じて long long 型などの使用を検討してください。
-
C++で平行四辺形を完成させる4点目の座標をすべて求める方法
本記事では、与えられた3つの座標から、面積がゼロではない平行四辺形を完成させるために必要な「4点目」の座標候補をすべて求める方法を解説します。A、B、Cという3点が与えられている場合、平行四辺形の構成として考えられるのは、次の3パターンだけです。 AB と AC を辺とし、BC を対角線とするケース AB と BC を辺とし、AC を対角線とするケース BC と AC を辺とし、AB を対角線とするケース つまり、3つの座標が決まれば、平行四辺形を形成できる4点目の座標は最大でも3通りしか存在しません。平行四辺形には「向かい合う辺は平行で長さが等しい」という重要な性質があるため、AD =
-
C++で配列のすべての部分集合(サブセット)の合計を求める方法
問題の概要整数の配列が与えられたとき、その部分集合(サブセット)から作り出せるすべての異なる合計値を求め、昇順に出力する方法を解説します。この問題は、配列の要素の合計値が比較的小さい場合に、動的計画法を使って効率的に解くことができます。例として、配列 [1, 2, 3] を考えてみましょう。考えられるすべての部分集合は {}、{1}、{2}、{3}、{1, 2}、{2, 3}、{1, 3}、{1, 2, 3} であり、それぞれの合計値は 0, 1, 2, 3, 3, 5, 4, 6 となります。重複する値を取り除くと、出力は 0, 1, 2, 3, 4, 5, 6 となります。アプローチ:動的