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

C++で同じ平均値を持つ2つのグループに配列を分割できるか判定する方法

配列 A が与えられたとします。この配列のすべての要素を、リスト B またはリスト C のいずれかに振り分ける必要があります(初期状態では BC はともに空です)。振り分け後、BC がどちらも空でないという条件のもとで、両者の平均値が等しくなるような振り分けが存在するかどうかを判定します。

たとえば、入力が [1,2,3,4,5,6,7,8,9,10] の場合、答えは true になります。

解法の考え方

まず、この問題の背後にある数学的な性質を整理しましょう。配列全体の要素数を n、合計を S とします。あるグループが k 個の要素を持ち、その合計が s であるとき、両グループの平均が等しくなる条件は次のように表せます。

s / k = (S − s) / (n − k)

これを変形すると s × n = S × k となります。つまり、「k 個の要素を選んだとき、その合計が S × k / n に一致するような組み合わせが存在するか」を調べればよいことになります。

アルゴリズムの手順

  • n を配列 A のサイズ、total を 0 として初期化します。
  • i を 0 から n−1 まで動かしながら total に各要素を加算し、配列の総和を求めます。
  • isPossible を false、mn / 2 に設定します。
  • i を 1 から m まで動かし、(total * i) % n == 0 となる i が存在すれば isPossible を true にします。これは、選ぶ要素数 k に対して目標となる合計 S × k / n が整数になり得るかどうかの事前チェックです。
  • isPossible が false のままの場合、条件を満たす分割は不可能なので false を返します。
  • サイズ (total + 1) × ((n / 2) + 1) の2次元DPテーブル dp を定義します。dp[j][l] は「l 個の要素を選んで合計 j を作れるかどうか」を表します。
  • dp[0][0] = true と初期化します。
  • 各要素 x について、j を total から x まで逆順に、l を 1 から n/2 まで動かしながら dp[j][l] = dp[j][l] || dp[j - x][l - 1] によりテーブルを更新します。
  • 最後に i を 1 から n/2 まで動かし、(total * i) % n == 0 かつ dp[total * i / n][i] が true であれば true を返します。
  • 該当するものが見つからなければ false を返します。

以下の実装例を見ると、より理解が深まるでしょう。

実装例(C++)

#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
   bool splitArraySameAverage(vector<int>& A) {
      int n = A.size();
      int total = 0 ;
      for(int i = 0; i < n; i++) total += A[i];
      bool isPossible = false;
      int m = n / 2;
      for (int i = 1; i <= m && !isPossible; ++i)
      if (total*i%n == 0) isPossible = true;
      if (!isPossible) return false;
      vector < vector <bool> > dp(total + 1, vector <bool>((n / 2) + 1));
      dp[0][0] = true;
      for(int i = 0; i < n; i++){
         int x = A[i];
         for(int j = total; j >= x; j--){
            for(int l = 1; l <= (n / 2); l++){
               dp[j][l] = dp[j][l] || dp[j - x][l - 1];
            }
         }
      }
      for(int i = 1 ; i <= (n / 2); i++){
         if((total * i) % n == 0 && dp[total * i / n][i]) return true;
      }
      return false;
   }
};
main(){
   Solution ob;
   vector<int> v = {1,2,3,4,5,6,7,8,9,10};
   cout << (ob.splitArraySameAverage(v));
}

入力

{1,2,3,4,5,6,7,8,9,10}

出力

1

  1. C++で左右の偶数・奇数の出現回数が一致する配列インデックスを見つける方法

    問題の概要 ここで取り上げるのは次のような問題です。n個の要素を持つ配列が与えられたとき、「あるインデックスの左側にある偶数の出現回数と右側にある偶数の出現回数が等しい」、または「左側にある奇数の出現回数と右側にある奇数の出現回数が等しい」という条件を満たすインデックスを1つ見つけます。該当するインデックスが存在しない場合は -1 を返します。 例として、配列が {4, 3, 2, 1, 2, 4} の場合を考えてみましょう。このとき答えは 2 になります。インデックス2の要素は「2」であり、その左側には奇数が1つ(3)、右側にも奇数が1つ(1)しか存在しないためです。 解決のアプローチ こ

  2. 【C++入門】変数と実数を使って配列を初期化する方法を徹底解説

    配列とは配列とは、連続したメモリ領域上に同じ型の要素をまとめて格納したデータ構造です。配列内で最も小さいアドレスは先頭の要素に対応し、最も大きいアドレスは末尾の要素に対応します。また、配列のインデックス(添字)は 0 から始まり、最大で「配列サイズ - 1」までとなります。C++では、配列を初期化する際に変数だけでなく、実数(数値リテラル)を直接指定することもできます。以下のサンプルプログラムで、その具体的な方法を見ていきましょう。サンプルコード#include <iostream>using namespace std;int main() {