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

C++で最大和ビトニック部分列を求める方法【動的計画法で解説】


この問題では、整数の配列 arr[] が与えられ、C++ を使って「最大和ビトニック部分列(Maximum Sum Bitonic Subsequence)」を求めるプログラムを作成します。

ビトニック部分列(Bi-tonic subsequence)とは、要素がまず増加し続け、途中から減少に転じるという特性を持つ特別な部分列のことです。

問題を理解するための例

入力

arr[] = {4, 2, 3, 7, 9, 6, 3, 5, 1}

出力

33

説明

合計が最大となるビトニック部分列は {2, 3, 7, 9, 6, 5, 1} です。

合計 = 2 + 3 + 7 + 9 + 6 + 5 + 1 = 33

解法のアプローチ

最大和ビトニック部分列を効率よく求めるために、incSeq[] と decSeq[] という2つの補助配列を用意します。

  • incSeq[i]: インデックス i を終点とする、arr[0...i] の範囲で厳密に増加する部分列の最大合計
  • decSeq[i]: インデックス i を始点とする、arr[i...n-1] の範囲で厳密に減少する部分列の最大合計

すべての計算が終わったら、(incSeq[i] + decSeq[i] - arr[i]) の最大値を maxSum として返します。arr[i] を引く理由は、ビトニック列の頂点となる要素 arr[i] が incSeq と decSeq の両方に含まれ、二重に加算されてしまうためです。

このアルゴリズムの計算量は O(N²)、必要な追加メモリは O(N) です。

実装例

上記の解法を示すプログラムは以下のとおりです。

#include <iostream>
using namespace std;
int calcMaxVal(int a, int b){
    if(a > b)
        return a;
    return b;
}
int findMaxSumBiTonicSubSeq(int arr[], int N){
    int maxSum = -1;
    int incSeq[N], decSeq[N];
    for (int i = 0; i < N; i++){
        decSeq[i] = arr[i];
        incSeq[i] = arr[i];
    }
    for (int i = 1; i < N; i++)
        for (int j = 0; j < i; j++)
            if (arr[i] > arr[j] && incSeq[i] < incSeq[j] + arr[i]) incSeq[i] = incSeq[j] + arr[i];
    for (int i = N - 2; i >= 0; i--)
        for (int j = N - 1; j > i; j--)
            if (arr[i] > arr[j] && decSeq[i] < decSeq[j] + arr[i])
            decSeq[i] = decSeq[j] + arr[i];
    for (int i = 0; i < N; i++)
        maxSum = calcMaxVal(maxSum, (decSeq[i] + incSeq[i] - arr[i]));
    return maxSum;
}
int main(){
    int arr[] = {4, 2, 3, 7, 9, 6, 3, 5, 1};
    int N = sizeof(arr) / sizeof(arr[0]);
    cout<<"The Maximum Sum of Bi-tonic subsequence is : "<<findMaxSumBiTonicSubSeq(arr, N);
    return 0;
}

出力

The Maximum Sum of Bi-tonic subsequence is : 33
  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 を境にし