C++で平均値の合計を最大化する方法|動的計画法による解説
問題概要
数列 A を最大 K 個の隣接するグループに分割することを考えます。このとき、スコアは「各グループの平均値の合計」として定義されます。目的は、達成可能な最大のスコアを求めることです。
例として、A = [9, 1, 2, 3, 9]、K = 3 の場合を見てみましょう。最適な分割は [9]、[1, 2, 3]、[9] であり、このときのスコアは次のように計算できます。
9 + (1 + 2 + 3) / 3 + 9 = 20
一方、[9, 1]、[2]、[3, 9] のように分割すると、得られるスコアはこれより小さくなります。
解法のアプローチ:動的計画法(メモ化再帰)
この問題は、動的計画法(DP)とメモ化再帰を組み合わせることで効率的に解けます。「インデックス idx 以降の要素を、残り k 個のグループに分割したときの最大スコア」を状態として定義し、再帰的に計算していきます。
アルゴリズムの手順
- 二次元の DP テーブル dp を定義する
- 再帰関数 solve() を定義する(引数:配列 A、現在のインデックス、残りのグループ数 k)
- idx が配列 A のサイズ以上なら 0 を返す(すべての要素を使い切った状態)
- k が 0 なら -100000 を返す(グループ数が足りない不正な状態を表す)
- dp[idx][k] が -1 以外(計算済み)なら、その値を返して再計算を回避する
- ret を負の無限大、sum を 0 で初期化する
- i を idx から配列の末尾までループさせる
- sum に A[i] を加算する
- ret を「sum / (i − idx + 1) + solve(A, i + 1, k − 1)」と比較し、大きい方を採用する
- dp[idx][k] に ret を格納して返す
メイン関数での処理
- n を配列 A のサイズとする
- dp を n × (K + 1) のサイズで作成し、すべて -1 で初期化する
- solve(A, 0, K) の結果を返す
C++ による実装例
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
vector < vector <double> > dp;
double solve(vector <int>& A, int idx, int k){
if(idx >= A.size()) return 0;
if(!k) return -100000;
if(dp[idx][k] != -1) return dp[idx][k];
double ret = INT_MIN;
double sum = 0;
for(int i = idx; i < A.size(); i++){
sum += A[i];
ret = max(sum / (i - idx + 1) + solve(A, i + 1, k - 1), ret);
}
return dp[idx][k] = ret;
}
double largestSumOfAverages(vector<int>& A, int K) {
int n = A.size();
dp = vector < vector <double> > (n, vector <double>(K + 1, -1));
return solve(A, 0, K);
}
};
main(){
vector<int> v = {9,1,2,3,9};
Solution ob;
cout << (ob.largestSumOfAverages(v, 3));
}
入力
[9,1,2,3,9] 3
出力
20
計算量の評価
状態の総数は n × K 個であり、各状態の計算に O(n) の時間がかかるため、時間計算量は O(n² × K) となります。また、DP テーブルの分だけメモリを必要とするため、空間計算量は O(n × K) です。メモ化によって同じ状態の再計算が不要になるため、単純な全探索と比べて大幅に高速化できるのがポイントです。
-
C++でnの約数のうち桁和が最大となる値を求めるアルゴリズム
この記事では、整数 n が与えられたときに、n のすべての約数の中で桁の合計(桁和)が最大となる値を求める問題を解説します。基本的な O(n) の解法から、√n を活用した効率的な O(√n) の解法まで、C++ のサンプルコードとともに見ていきましょう。 問題の概要 与えられた整数 n の約数をすべて列挙し、それぞれの桁和を計算します。そして、その中で最も大きな桁和を答えとして返すのが目的です。 入出力例 入力: 18 出力: 9 解説: 18 の約数は 1, 2, 3, 6, 9, 18 です。それぞれの桁和を計算すると、1, 2, 3, 6, 9, 9 となり、最大値は 9 であるこ
-
C++で二分木における最大部分木の合計を求める方法
この問題では、二分木(バイナリツリー)が与えられます。私たちのタスクは、木の中で最も大きな合計値を持つ部分木を見つけることです。 問題の概要 二分木には正の値と負の値が混在しています。その中から、ノードの合計が最大になる部分木を特定する必要があります。 例で問題を理解しよう 出力: 13 説明: 左部分木の合計:7 右部分木の合計:1 木全体の合計:13 このように、根を含む木全体の合計である「13」が最大の部分木の合計となります。 解法のアプローチ この問題を解くためには、後順走査(ポストオーダー走査)を利用します。手順は以下の通りです。 左部分木と右部分木それぞれのノードの合計を再