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

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)のアプローチと比べても、非常に効率的な解法です。

  1. 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]

  2. 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)を追跡しながら配列を一度だけ