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

C++で最大和の交互部分列を求めるアルゴリズムと実装方法

はじめに

本記事では、C++を用いて「最大和の交互部分列(Alternating Subsequence)」を求めるプログラムについて解説します。

ここで扱う問題は、整数型の配列が与えられたとき、その中から「減少→増加→減少→…」というように増減が交互に入れ替わる部分列を抜き出し、その要素の合計値が最大となるものを見つけるというものです。

アルゴリズムの考え方

この問題は動的計画法(DP)を用いて解くことができます。具体的には、以下の2つの配列を用意します。

  • dec[i]: i番目の要素で終わる交互部分列のうち、「直前の操作が減少」である場合の最大和
  • inc[i]: i番目の要素で終わる交互部分列のうち、「直前の操作が増加」である場合の最大和

各インデックスiについて、それより前のすべてのインデックスjを走査し、arr[j] > arr[i] のときは減少遷移として dec[i] を更新し、arr[j] < arr[i] のときは増加遷移として inc[i] を更新していきます。最終的に dec 配列と inc 配列全体の中で最大の値が答えとなります。

実装例(C++)

#include<bits/stdc++.h>
using namespace std;
// 最大和の交互部分列を返す関数
int maxAlternateSum(int arr[], int n) {
   if (n == 1) return arr[0];
   int dec[n];
   memset(dec, 0, sizeof(dec));
   int inc[n];
   memset(inc, 0, sizeof(inc));
   dec[0] = inc[0] = arr[0];
   int flag = 0;
   for (int i = 1; i < n; i++) {
      for (int j = 0; j < i; j++) {
         if (arr[j] > arr[i]) { dec[i] = max(dec[i], inc[j] + arr[i]); flag = 1; }
         else if (arr[j] < arr[i] && flag == 1) inc[i] = max(inc[i], dec[j] + arr[i]);
    }
  }
   int result = INT_MIN;
   for (int i = 0; i < n; i++) {
      if (result < inc[i])
      result = inc[i];
      if (result < dec[i]) result = dec[i];
  }
   return result;
}
int main() {
   int arr[] = {8, 2, 3, 5, 7, 9, 10};
   int n = sizeof(arr)/sizeof(arr[0]);
   cout << "Maximum sum = " << maxAlternateSum(arr , n ) << endl;
   return 0;
}

実行結果

Maximum sum = 25

コードの解説

上記のサンプルでは、配列 {8, 2, 3, 5, 7, 9, 10} が与えられています。この場合、例えば「8 → 2 → 10」という交互部分列を選ぶと、8 + 2 + 10 = 20 となります。しかし、より大きな和を得るには「8 → 2 → 3 → 2 …」のような組み合わせではなく、条件を満たす範囲で最大の要素を選ぶ必要があります。結果として得られる最大和は 25 です。

計算量について

この実装は二重ループを使用しているため、時間計算量は O(n²)、補助配列分の空間計算量は O(n) となります。配列サイズが大きい場合は、O(n) で解く最適化手法(各要素を加算・減算する貪欲法ベースのアプローチなど)も検討するとよいでしょう。

まとめ

C++における最大和交互部分列の問題は、動的計画法を使うことで効率的に解くことができます。「増加」と「減少」の状態を2つの配列で管理するというシンプルな発想がポイントです。ぜひ自身のコードにも応用してみてください。

  1. C++のプレフィックス和(累積和)を活用してO(n)で最大部分配列和を求める方法

    問題概要 正の整数と負の整数が混在する配列が与えられたとき、その配列の中で合計値が最大となる部分配列(連続した要素の並び)の合計を求める問題です。 例 入力配列が {-12, -5, 4, -1, -7, 1, 8, -3} の場合、合計が最大になる部分配列は {1, 8} となるため、出力は 9 になります。 アルゴリズム この問題は、プレフィックス和(累積和)を利用することで O(n) の時間計算量で効率的に解くことができます。考え方の核心は、「ある位置 i で終わる部分配列の合計の最大値」は「prefix_sum[i] から、それ以前に現れた最小の累積和を引いた値」で表せるという点です

  2. C++で配列の最大平衡和(イクリブリアム・サム)を求める方法

    問題概要配列 arr[] が与えられたとき、あるインデックス i における「接頭辞和(プレフィックスサム)」と「接尾辞和(サフィックスサム)」が一致する値の中から、最大値を見つけるのがこの問題の目的です。この一致する値は「平衡和(イクリブリアム・サム)」と呼ばれます。例入力配列が以下の場合を考えてみましょう。Arr[] = {1, 2, 3, 5, 3, 2, 1}このとき出力は 11 になります。その理由は次の通りです。接頭辞和 = arr[0..3] = 1 + 2 + 3 + 5 = 11接尾辞和 = arr[3..6] = 5 + 3 + 2 + 1 = 11インデックス 3 を境にし