C++で長さkの部分配列の最大平均を求める方法(スライディングウィンドウ)
この記事では、n個の要素を持つ配列から、指定された長さkの連続する部分配列の中で平均値が最大となるものを見つけ、その最大平均値を返す方法を解説します。
問題の概要
例として、入力が [1,13,-5,-8,48,3]、k = 4 の場合を考えてみましょう。このとき出力は 12.0 になります。これは (13 + (-5) + (-8) + 48) / 4 = 12.0 となるためです。
アルゴリズム:スライディングウィンドウ
この問題は「スライディングウィンドウ(滑動窓)」という手法を使うと効率的に解けます。毎回k個の要素を足し直す代わりに、ウィンドウを1つずらすたびに「新しい要素を加え、古い要素を引く」だけで合計を更新できるのがポイントです。計算量はO(n)に抑えられます。
手順は以下の通りです。
- 最初のk個の要素の合計 sum を計算します。
- maxi := sum として初期値を設定します。
- i を k から配列の末尾まで進めながら、sum := sum + nums[i] − nums[i − k] でウィンドウ内の合計を更新します。
- sum > maxi であれば、maxi := sum と更新します。
- 最後に maxi / k を返します。
C++での実装例
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
double findMaxAverage(vector<int>& nums, int k) {
int sum = 0;
for (int i = 0; i < k; i++) {
sum += nums[i];
}
double maxi = sum;
for (int i = k; i < nums.size(); i++) {
sum += nums[i] - nums[i - k];
if (sum > maxi) {
maxi = sum;
}
}
return maxi / k;
}
};
main(){
Solution ob;
vector<int> v = {1,13,-5,-8,48,3};
cout << (ob.findMaxAverage(v, 4));
}入力例
{1,13,-5,-8,48,3}, 4出力例
12
計算量について
この実装では、最初のウィンドウの合計計算にO(k)、その後のループは配列の残りの要素に対して1回ずつ処理するだけなので、全体の時間計算量はO(n)、追加のメモリ使用量はO(1)となります。全ての部分配列の平均を総当たりで調べるO(n·k)のアプローチと比べても、非常に効率的な解法です。
-
C++で配列を最大K個に分割して平均の合計を最大化する方法
問題概要 数値の配列 A が与えられます。この配列を最大 K 個の隣接する(空でない)グループに分割し、スコアを「各グループの平均値の合計」と定義します。このとき、達成できる最大スコアを求めるのが本問題です。 入力例 入力配列が {9, 2, 5, 3, 10} の場合、たとえば次のように分割できます。 {9} {2, 5, 3} {10} このときの平均の合計は次のとおりです。 9 + (2 + 5 + 3) / 3 + 10 = 22.33 アルゴリズム(メモ化再帰) この問題は、メモ化(記憶化)再帰を使うことで効率よく解くことができます。 memo[i][k]:A[i]〜A[n-1]
-
C++で厳密に増加する部分配列の最大和を求めるアルゴリズム
問題の概要n 個の整数からなる配列が与えられたとき、その中に存在する「厳密に増加する(strictly increasing)部分配列」の中で、要素の合計が最大となるものを求めます。例として、次のような配列を考えてみましょう。[1, 2, 3, 2, 5, 1, 7]この配列には、厳密に増加している部分配列が3つ存在します。{1, 2, 3}{2, 5}{1, 7}それぞれの合計は 6、7、8 となり、この中で最大となるのは {1, 7} の合計 8 です。解き方の考え方この問題は、現在の部分配列の合計(current_sum)とこれまでの最大合計(max_sum)を追跡しながら配列を一度だけ