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

C++プログラム:配列内で要素間の距離がK未満にならない部分列の最大合計を求める方法

この問題では、サイズnの配列arr[]と整数kが与えられます。求めるのは、配列内でどの2つの要素も距離がk未満にならないように選んだ部分列(サブシーケンス)の合計の最大値です。

問題の概要

配列から要素を選んで部分列を作るとき、選んだ任意の2つの要素のインデックス差がk以上になるようにする必要があります。その制約のもとで、部分列の要素の合計が最大になる組み合わせを見つけるのが目的です。

入力例

arr[] = {6, 2, 5, 1, 9, 11, 4}
k = 2

出力例

16

説明

条件を満たす部分列の候補:
{6, 1, 4} → 合計 = 11
{2, 9} → 合計 = 11
{5, 11} → 合計 = 16
{1, 4} → 合計 = 5
...
最大合計 = 16

解法アプローチ:動的計画法(DP)

この問題は動的計画法を使うことで効率的に解けます。DP[i]には「i番目の要素までを考慮したときの、条件を満たす部分列の最大合計」を格納します。

i番目の要素について、「現在の要素arr[i]を部分列に加えることで合計が増えるかどうか」を判定します。具体的には、直前のDP値と「k+1個前までのDP値+現在の要素」を比較します。

if( DP[i − (k+1)] + arr[i] > DP[i − 1] )
→ DP[i] = DP[i − (k+1)] + arr[i]
それ以外の場合 → DP[i] = DP[i−1]

最終的に、DP配列の中で最も大きい値が答えとなります。

アルゴリズムの手順

初期化

maxSumSubSeq = −1、maxSumDP[n]

ステップ1

maxSumDP[0] = arr[0] と初期化する。

ステップ2

i を 1 から n−1 までループさせる。

ステップ2.1

i < k の場合:
maxSumDP[i] = max(arr[i], maxSumDP[i−1])

ステップ2.2

それ以外の場合:
maxSumDP[i] = max(arr[i], maxSumDP[i − (k+1)] + arr[i])

ステップ3

maxSumDPの全要素から最大値を求め、maxSumSubSeqに格納する。

ステップ4

maxSumSubSeq を返す。

C++による実装例

#include <iostream>
using namespace std;

int retMaxVal(int a, int b){
    if(a > b)
    return a;
    return b;
}

int calcMaxSumSubSeq(int arr[], int k, int n) {
    int maxSumDP[n];
    int maxSum = −1;
    maxSumDP[0] = arr[0];
    for (int i = 1; i < n; i++){
       if(i < k ){
          maxSumDP[i] = retMaxVal(arr[i], maxSumDP[i − 1]);
       }
       else
       maxSumDP[i] = retMaxVal(arr[i], maxSumDP[i − (k + 1)] + arr[i]);
   }
    for(int i = 0; i < n; i++)
    maxSum = retMaxVal(maxSumDP[i], maxSum);
    return maxSum;
}

int main() {
    int arr[] = {6, 2, 5, 1, 9, 11, 4};
    int n = sizeof(arr) / sizeof(arr[0]);
    int k = 2;
    cout<<"配列内で2つの要素が距離 "<<k<<" 未満にならないような部分列の最大合計は "<<calcMaxSumSubSeq(arr, k, n);
    return 0;
}

実行結果

配列内で2つの要素が距離 2 未満にならないような部分列の最大合計は 16

計算量の分析

このアルゴリズムの時間計算量はO(n)です。配列を一度走査してDPテーブルを埋め、もう一度走査して最大値を求めるだけだからです。空間計算量もO(n)で、DPテーブルの分だけ追加メモリが必要になります。貪欲法では局所最適解に陥る可能性があるため、この種の「間隔制約付き最大化問題」では動的計画法が有効なアプローチとなります。

  1. 【C++】循環配列で隣接しない要素を選んだときの最大合計を求める方法

    問題の概要本記事では、循環配列 cirArr[] が与えられたとき、「どの2つの要素も隣接して選ばない」という条件を満たす要素の最大合計を求めるプログラムをC++で作成します。問題の詳細循環配列に対して、隣接する要素を同時に選ぶことができない、つまり要素を一つ飛ばしで選択した場合の最大合計を求める必要があります。循環配列とは、配列の末尾の要素が先頭の要素につながっている特殊な配列構造のことです。具体例で問題を確認しましょう。入力例cirArr[] = {4, 1, 5, 3, 2}出力例9解説最大の合計となる循環部分列は [4, 5, 2] で、その合計は 9 になります。解決アプローチこの問

  2. 配列の全要素を乗算するC++プログラムの解説

    整数型の要素を持つ配列が与えられたとき、配列内のすべての要素を掛け合わせ、その積を表示することを考えます。本記事では、この問題をC++(C言語スタイルのコード)で解く方法を、アプローチ、アルゴリズム、サンプルコード、実行結果まで順を追って解説します。 例 入力: arr[]={1,2,3,4,5,6,7} 出力: 1 x 2 x 3 x 4 x 5 x 6 x 7 = 5040 入力: arr[]={3, 4, 6, 2, 7, 8, 4} 出力: 3 x 4 x 6 x 2 x 7 x 8 x 4 = 32256 解き方のアプローチ この問題は、累積用の一時変数を用意し、配列の要素を先頭