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

【C++】合計が3の倍数になるサイズ2・3のグループの数を効率的に数える方法

はじめに

この記事では、与えられた配列の中から「要素の合計が3の倍数になる」サイズ2または3のグループがいくつ作れるかを求めるC++プログラムを解説します。

問題の概要

整数の配列が与えられたとき、以下の条件を満たすグループの総数を数えます。

  • グループのサイズは2つまたは3つの要素
  • グループ内の要素の合計が3で割り切れること

例として、配列 {3, 6, 7, 2, 9} を考えます。この場合、条件を満たすグループは8通り存在します。

アルゴリズムのポイント

すべての組み合わせを総当たりで調べると計算コストが大きくなりますが、各要素を3で割った余り(0・1・2)ごとの出現回数を数えれば、組み合わせの数を数式で直接求められます。

合計が3の倍数になるのは、余りの組み合わせが次の場合です。

  • サイズ2: (0, 0) または (1, 2)
  • サイズ3: (0, 0, 0)、(1, 1, 1)、(2, 2, 2)、(0, 1, 2)

C++での実装例

#include<bits/stdc++.h>
using namespace std;
// サイズ2または3のグループ数を返す関数
int count_groups(int arr[], int n){
   int c[3] = {0}, i;
   int res = 0;
   // 余りごとに要素数をカウント
   for (i=0; i<n; i++)
      c[arr[i]%3]++;
   // サイズ2のグループ
   res += ((c[0]*(c[0]-1))>>1);           // (0, 0)
   res += c[1] * c[2];                    //(1, 2)
   // サイズ3のグループ
   res += (c[0] * (c[0]-1) * (c[0]-2))/6;  //(0, 0, 0)
   res += (c[1] * (c[1]-1) * (c[1]-2))/6;  //(1, 1, 1)
   res += ((c[2]*(c[2]-1)*(c[2]-2))/6);    //(2, 2, 2)
   res += c[0]*c[1]*c[2];                  //(0, 1, 2)
   return res;
}
int main(){
   int arr[] = {3, 6, 7, 2, 9};
   int n = sizeof(arr)/sizeof(arr[0]);
   cout << "Required number of groups are " << count_groups(arr,n) << endl;
   return 0;
}

実行結果

Required number of groups are 8

コードの解説

まず、ループで各要素を3で割った余りをインデックスとして使い、c[0]、c[1]、c[2]にそれぞれの出現回数を記録します。サンプル配列 {3, 6, 7, 2, 9} の場合、余りは順に 0, 0, 1, 2, 0 となるため、c[0]=3、c[1]=1、c[2]=1 となります。

続いて、組み合わせの公式を使って各パターンのグループ数を加算します。

  • 同じ余り0から2つ選ぶ組合せ:C(c[0], 2) = c[0]×(c[0]-1)/2
  • 余り1と余り2を1つずつ選ぶ:c[1]×c[2]
  • 同じ余りから3つ選ぶ組合せ:C(c[i], 3) = c[i]×(c[i]-1)×(c[i]-2)/6
  • 余り0・1・2から1つずつ選ぶ:c[0]×c[1]×c[2]

これらを合計すると、サンプルでは 3+1+1+3 = 8 という結果が得られます。

計算量

この手法では、余りの分類にO(n)、その後の集計は定数時間で完了するため、全体の計算量はO(n)です。全組み合わせを調べるO(n³)のアプローチと比べて大幅に高速であり、大規模な配列でも実用的に動作します。

まとめ

剰余ごとの出現回数を利用することで、合計が3の倍数になるサイズ2・3のグループ数を線形時間で効率よく求められます。この考え方は、「合計が特定の数で割り切れるペアやグループを探す」問題全般に応用できる汎用的なテクニックです。

  1. C++でサイズnの配列からr個の要素を選ぶすべての組み合わせを出力する方法

    この記事では、サイズnの配列と正の整数rが与えられたとき、配列の要素から選んだサイズrのすべての組み合わせを出力する方法を解説します。具体例を見て、問題のイメージをつかみましょう。入力: {5, 6, 7, 8} ; r = 3 出力: {5, 6, 7}, {5, 6, 8}, {5, 7, 8}, {6, 7, 8}解法1:要素を固定して再帰的に探索するこの問題に対する基本的なアプローチは、一部の要素を固定し、残りの要素に対して再帰(またはループ)を回してすべての組み合わせを見つけるというものです。ポイントは、先頭の n-r+1 個の要素だけを固定対象にすればよいという点です。それ以降の要

  2. C++で配列のすべての部分集合の合計値を効率的に求める方法

    n個の要素を持つ配列Aが与えられたとき、その配列のすべての部分集合の合計値の総和を求める問題を考えてみましょう。例えば、配列が A = [5, 6, 8] の場合、各部分集合とその合計は以下のようになります。部分集合合計5566885, 6116, 8145, 8135, 6, 819総和76考え方のポイントn個の要素を持つ配列の場合、部分集合の数は(空集合を含めて)2n 個存在します。ここで重要な性質に注目してみましょう。各要素は、ちょうど 2(n−1) 回だけ部分集合に出現するという点です。この性質を利用すると、すべての部分集合の合計値の総和は次の式で一発に計算できます。総和 = (配列の