1 サブシーケンスと昇順サブシーケンス
1.1 子序列
シーケンス A={a1,a2,...an} はいくつかの項目を任意に削除し、残りのシーケンスは A のサブシーケンスと呼ばれます。たとえば、シーケンス A={1,3,5,4,2}、3 番目と 5 番目のアイテムを削除、シーケンス B={1,3,4} を取得、3 番目と 4 番目のアイテムを削除、シーケンス C= を取得{1, 3, 2}、この時点でシーケンス B と C はシーケンス A のサブシーケンスです。
1.2 昇順サブシーケンス
シーケンス内の要素が小さいものから大きいものへと配置されている場合、シーケンスは昇順であり、シーケンスが別のシーケンスのサブシーケンスである場合、それは昇順サブシーケンスと呼ばれます。たとえば、「1.1 サブシーケンス」で言及されている B は A の昇順サブシーケンスであり、C は A のサブシーケンスですが、昇順サブシーケンスではありません。
1.3 最長の昇順サブシーケンス
最も多くの要素を含む昇順のサブシーケンスは、最長の昇順のサブシーケンスと呼ばれます。たとえば、シーケンス D={1,5} はシーケンス A の昇順のサブシーケンスですが、最長の昇順のサブシーケンスではありませんが、シーケンス B はシーケンス A の最長の昇順のサブシーケンスです。
2 最長昇順部分列を解く動的計画法
2.1 プロセス
シーケンスの最長昇順サブシーケンス問題を解くプロセスを図 1 に示します。
図 1 最長昇順部分列を解くプロセス
図 1 からわかるように、素数群の要素をトラバースするとき、要素の値が要素の前の要素の値よりも大きい場合、昇順の部分列を形成することができます。前の要素の長さに対応する最長のサブシーケンスを見つけ、これらの長さの最大値を見つけ、現在の要素に対応する最長のサブシーケンスである最大値に 1 を加える必要があります。上記の分析から、動的計画法が最長の昇順部分列を解くための「状態遷移方程式」は次のようになることがわかります。
dp(n) = max(dp(1),dp(2),...dp(n-1))+1
このうち、n は配列内の要素の位置、つまりインデックス値を表します。
2.2 コアコード
シーケンスの最も長い昇順のサブシーケンスのコア コードは次のとおりです。
for (int i = 1; i <= n; i++)
{
dp[i] = 1;
for (int j = 1; j <= i - 1; j++)
{
if (a[i] > a[j])
{
dp[i] = max(dp[i], dp[j]+1);
}
}
length = max(length, dp[i]);
}
その中で、最初のループは配列内のすべての要素をトラバースすることを意味し、2 番目のループはその要素の前のすべての要素をトラバースすることを意味し、コードの 8 行目は「状態遷移方程式」であり、コードの 11 行目の関数は次を見つけることです。配列内のすべての要素 対応する最大値、つまり、最も長い昇順のサブシーケンスの長さ。
2.3 完全なコード
シーケンスの最長昇順サブシーケンスの完全なコードは次のとおりです。
#include <iostream>
using namespace std;
int main()
{
int a[10001] = { 0 };
int dp[10001] = { 0 };
int n;
int length = 0;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 1; i <= n; i++)
{
dp[i] = 1;
for (int j = 1; j <= i - 1; j++)
{
if (a[i] > a[j])
{
dp[i] = max(dp[i], dp[j]+1);
}
}
length = max(length, dp[i]);
}
cout<<(length);
return 0;
}