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

O(n²)の計算量で最大部分配列の和を求めるC++プログラム(素朴な手法)

本記事では、O(n²)の計算量で配列内の最大部分配列の和を求めるC++プログラムを紹介します。この手法は「素朴な手法(ナイーブ法)」と呼ばれ、あらゆる長さの部分配列を効率よく走査することで最大値を導き出します。

アルゴリズム

開始
    配列の要素を入力として受け取る。
    部分配列の長さを 1 から n まで変化させるループを作成する。
    そのループの中に、さらにネストした別のループを作り、
    その長さにおける最初の部分配列の和を計算する。
    残りの部分配列の和については、直前の和に次の要素を加え、
    ウィンドウから外れる先頭の要素を引くことで効率的に求める。
    求めた値を全体の最大値と比較し、より大きければ更新する。
    最後に、最大の部分配列とその和を出力する。
終了。

アルゴリズムのポイント

この手法の鍵となるのは「スライディングウィンドウ」の考え方です。同じ長さの部分配列を次々と調べる際に、毎回ゼロから合計を計算し直すのではなく、直前の部分配列の和を再利用します。具体的には、新しい要素を加算し、範囲外となる先頭の要素を減算するだけで、次の部分配列の和を O(1) で求められます。これにより、各長さごとの走査が O(n)、長さの組み合わせが n 通りあるため、全体の計算量は O(n²) に収まります。

サンプルコード

#include<iostream>
using namespace std;
int main() {
    int n, i, j, m=-1, s, ini_m, fi_m;
    cout<<"\nEnter the number of data element in the array: ";
    cin>>n;
    int a[n];
    for(i = 0; i < n; i++) {
        cout<<"Enter element "<<i+1<<": ";
        cin>>a[i];
    }
    for(i = 1; i < n+1; i++) {
        s = 0;
        for(j = 0; j < n; j++) {
            if(j < i)
                s += a[j];
            else
                s = s+a[j]-a[j-i];
            if(m< s) {
                ini_m = j-i+1;
                fi_m = j;
                m = s;
            }
        }
    }
    cout<<"\nThe maximum sub array is: ";
    for(i = ini_m; i <= fi_m; i++)
        cout<<a[i]<<" ";
        cout<<"\nThe maximum sub-array sum is: "<<m;
}

実行結果

Enter the number of data element in the array: 10
Enter element 1: 1
Enter element 2: -2
Enter element 3: 3
Enter element 4: -4
Enter element 5: 5
Enter element 6: -6
Enter element 7: 7
Enter element 8: 8
Enter element 9: -9
Enter element 10: 10
The maximum sub array is: 7 8 -9 10
The maximum sub-array sum is: 16

結果の解説と補足

上記の例では、配列 {1, -2, 3, -4, 5, -6, 7, 8, -9, 10} の中から、部分配列 {7, 8, -9, 10} の和である 16 が最大となることが確認できます。

なお、この問題は「カダネのアルゴリズム(Kadane's Algorithm)」を用いれば O(n) の計算量で解くことも可能です。O(n²) の素朴な手法は、アルゴリズムの動作を理解するうえで非常に有用な第一歩ですが、大規模なデータを扱う実運用では、より効率的な手法の採用を検討するとよいでしょう。

  1. C++で二分木の最も深い葉ノードの値の合計を求める方法

    はじめに二分木(バイナリツリー)が与えられたとき、その中で最も深い位置にある葉ノード(deepest leaves)の値の合計を求めることを考えます。例えば、次のような二分木があるとします。この場合、最も深い葉ノードは 7 と 4 であり、出力は 11 になります。解法のアプローチこの問題は、深さ優先探索(DFS)を用いて各レベルごとのノードの値の合計を記録し、最後に最大深度に対応する合計を取得することで解けます。具体的には、以下の手順に従います。レベルごとの合計を保持するマップ m と、最大深度を記録する変数 maxDepth を定義するノードとレベルを受け取る再帰メソッド solve()

  2. Pythonで部分配列の合計をmで割った余りの最大値を求めるプログラム

    問題の概要 n個の要素からなる配列 nums と整数 m が与えられたとき、任意の部分配列(連続する要素の集合)の合計を m で割った余りの最大値を求めることを考えます。 たとえば、nums = [1,5,7,3]、m = 5 が入力として与えられた場合、出力は 3 になります。すべての部分配列について余りを計算すると、次のようになります。 [1] mod 5 = 1 [5] mod 5 = 0 [7] mod 5 = 2 [3] mod 5 = 3 [1,5] mod 5 = 1 [5,7] mod 5 = 2 [7,3] mod 5 = 0 [1,5,7] mod 5 = 3 [5,7,