C++で解説:プレフィックス内の増加部分列の最大和の求め方(プレフィックス後の指定要素を必ず含む)
この問題では、N個の整数からなる配列 arr[] と、2つのインデックス x・y が与えられます。求めるのは「インデックス x までのプレフィックスから選んだ増加部分列の最大和」であり、プレフィックスより後ろにあるインデックス y の要素を必ず部分列に含める必要があります。
問題の概要
インデックス x までの範囲で増加部分列の最大和を求め、そのうえでインデックス y の要素を必ず含めます。
具体例で問題を確認しましょう。
入力
arr[] = {1, 5, 9, 131, 6, 100, 11, 215}, x = 4, y = 6
出力
26
説明
インデックス 3 までの部分列を選び、最後に arr[6] = 11 を含めます。
このとき部分列は {1, 5, 9, 11} となり、その和は 1+5+9+11 = 26 です。
解法アプローチ
1. シンプルな解法
まずインデックス x までの要素で新しい配列を作成し、その末尾にインデックス y の要素を追加します。続いてすべての増加部分列を計算し、arr[y] を含められないものを除外しながら最大和(maxSum)を求めます。ただし、この方法は組み合わせの数が膨大になりやすく、非効率です。
2. 動的計画法(DP)による解法
より効率的なのが動的計画法の活用です。2次元配列 DP[][] を用意し、増加部分列の最大和を格納していきます。DP[x][y] の値が「インデックス x までの範囲で arr[y] を含む増加部分列の最大和」に対応します。
実装例
この解法の動作を示すプログラムです。
#include <iostream>
using namespace std;
int DP[100][100];
void preCalcMaxSum(int arr[], int N){
for (int i = 0; i < N; i++) {
if (arr[i] > arr[0])
DP[0][i] = arr[i] + arr[0];
else
DP[0][i] = arr[i];
}
for (int i = 1; i < N; i++) {
for (int j = 0; j < N; j++) {
if (arr[j] > arr[i] && j > i) {
if (DP[i - 1][i] + arr[j] > DP[i - 1][j])
DP[i][j] = DP[i - 1][i] + arr[j];
else
DP[i][j] = DP[i - 1][j];
}
else
DP[i][j] = DP[i - 1][j];
}
}
}
int main() {
int arr[] = {1, 5, 9, 131, 6, 100, 11, 215};
int N = sizeof(arr) / sizeof(arr[0]);
int x = 4, y = 6;
preCalcMaxSum(arr, N);
cout<<"The maximum sum increasing subsequence from a prefix and a given element after prefix is must is ";
cout<<DP[x][y];
return 0;
}
出力
The maximum sum increasing subsequence from a prefix and a given element after prefix is must is 26
より効率的な解法
さらに効率を高めるには、「インデックス x までの範囲で、部分列の最大要素が arr[y] より小さくなるような増加部分列の最大和」を求める方法が有効です。ここでも動的計画法を利用します。この条件を満たす最大和を先に計算しておけば、最後に arr[y] を加えるだけで答えが得られます。
実装例
この解法の動作を示すプログラムです。
#include <iostream>
using namespace std;
int calcMaxSum(int arr[], int n, int x, int y){
int DP[x] = {0};
int maxSum = -1;
for (int i = 0; i <= x; i++)
DP[i] = arr[i];
for (int i = 0; i <= x; i++) {
if (arr[i] >= arr[y]) {
continue;
}
for (int j = 0; j < i; j++) {
if (arr[i] > arr[j])
DP[i] += arr[j];
maxSum = max(maxSum, DP[i]);
}
}
if (maxSum == -1) {
return arr[y];
}
return maxSum + arr[y];
}
int main(){
int arr[] = {1, 5, 9, 131, 6, 100, 11, 215};
int N = sizeof(arr) / sizeof(arr[0]);
int x = 4, y = 6;
cout<<"The maximum sum increasing subsequence from a prefix and a given element after prefix is must is ";
cout<<calcMaxSum(arr, N, x, y);
return 0;
}
出力
The maximum sum increasing subsequence from a prefix and a given element after prefix is must is 26
このように、動的計画法を用いることで、プレフィックス内の増加部分列の最大和を効率的に計算し、指定した要素 arr[y] を必ず含む解を求めることができます。全列挙による素朴なアプローチと比べ、計算量を大幅に抑えられる点が大きなメリットです。
-
C++で最大1要素を削除した後の部分配列の最大和を最大化する方法
問題文N個の整数からなる配列 arr[] が与えられます。まず最大部分配列和を求め、その部分配列から最大で1つの要素を削除したとき、合計が最大になるようにします。つまり、削除できる要素は最大で1つまでとし、削除後の合計を最大化することがこの問題の目的です。例として、入力配列が {1, 2, 3, -2, 3} の場合を考えてみましょう。この配列の最大部分配列は配列全体 {1, 2, 3, -2, 3} で、その和は7です。ここで -2 を削除すると、残りの配列は次のようになります。{1, 2, 3, 3} となり、和は9でこれが最大となります。アルゴリズムこの問題は、カダネのアルゴリズム(Ka
-
C++で解く:合計が指定値以下となる最大サイズ2の最小セット数
問題概要正の整数からなる配列 arr[] が与えられたとき、次の条件を満たす「セット」の最小数を求める問題です。1つのセットに含められる要素は最大2つまでです。2つの要素は配列内で隣接している必要はありません。セット内の要素の合計は、与えられたキー(Key)以下でなければなりません。なお、キーは配列内の最大要素以上であると仮定できます。例たとえば、arr[] = {1, 2, 3, 4}、k = 5 が与えられた場合、次の2つのペアを作成できます。{1, 4} と {2, 3}このように、4つの要素を合計が5以下になるペア2つに分割できるため、答えは「2」となります。アルゴリズムこの問題は、貪