【動的計画法】サブシーケンスシリーズ

動的計画法(部分系列)

1. 最長の増加サブシーケンス

トピックリンク

  1. ステータス表示

    dp[i]表示到 i 位置时,所有子序列当中最长的子序列的长度

  2. 状態遷移方程式

    画像-20230731141850644

  3. 初期化

    テーブル内のすべてのデータは 1 に初期化されます

  4. フォームに記入

    左から右へ

  5. 戻り値

    dp テーブル全体の最大値を返します。

ACコード:

class Solution 
{
    
    
public:
    int lengthOfLIS(vector<int>& nums) 
    {
    
    
        int n = nums.size();
        vector<int> dp(n, 1);
        int ret = 1;
        for (int i = 1; i < n; i++)
        {
    
    
            for (int j = 0; j < i; j++)
            {
    
    
                if (nums[i] > nums[j])
                {
    
    
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
            ret = max(ret, dp[i]);
        }
        return ret;
    }
};

2.ウォブルシーケンス

トピックリンク

  1. ステータス表示

    f[i]表示:以 i 位置为结尾的所有子序列当中,最后一个位置是上升的最长摆动序列的长度

    g[i]表示:以 i 位置为结尾的所有子序列当中,最后一个位置是下降的最长摆动序列的长度

  2. 状態遷移方程式

    gspg67wwyn-1690785946044.png

  3. 初期化

    テーブル内のすべての値は 1 に初期化されます

  4. フォームに記入

    左から右へ

  5. 戻り値

    2 つのテーブルの最大値を返します

ACコード:

class Solution 
{
    
    
public:
    int wiggleMaxLength(vector<int>& nums) 
    {
    
    
        int n = nums.size();
        vector<int> f(n, 1), g(n, 1);
        int ret = 1;
        for (int i = 1; i < n; i++)
        {
    
    
            for (int j = 0; j < i; j++)
            {
    
    
                if (nums[i] > nums[j]) f[i] = max(f[i], g[j] + 1);
                else if (nums[i] < nums[j]) g[i] = max(g[i], f[j] + 1);
            }
            ret = max(ret, max(f[i], g[i]));
        }
        return ret;
    }
};

3. 最も長く増加する部分列の数

トピックリンク

ここで考えが必要です。

配列を一度走査すると、最大の数値の出現回数を見つけることができます。

コード:

#include <iostream>

using namespace std;

int main()
{
    
    
	int arr[] = {
    
    2, 3, 1, 234, 43, 342, 234, 5, 34, 43, 8, 342};
	int n = sizeof(arr)/sizeof(arr[0]);
	int maxval = 0;
	int count = 0;
	for (int i = 0; i < n; i++)
	{
    
    
		if (arr[i] > maxval) maxval = arr[i], count = 1;
		else if (arr[i] == maxval) count++;
	}
	cout << maxval << endl;
	cout << count << endl;
	return 0;
}
  1. ステータス表示

    len[i]表示以 i 位置为结尾所有子序列当中,最长递增子序列的长度

    count[i]表示以 i 位置为结尾所有子序列当中,最长递增子序列的个数

  2. 状態遷移方程式

    c46gcsfr8s-1690789206047.png

  3. 初期化

    すべての値は 1 に初期化されます

  4. フォームに記入

    左から右へ

  5. 戻り値

    カウントテーブルの最後のもの

ACコード:

class Solution 
{
    
    
public:
    int findNumberOfLIS(vector<int>& nums) 
    {
    
    
        int n = nums.size();
        vector<int> len(n, 1), count(n, 1);
        int retval = 1, retcount = 1;
        for (int i = 1; i < n; i++)
        {
    
    
            for (int j = 0; j < i; j++)
            {
    
    
                if (nums[i] > nums[j])
                {
    
    
                    if (len[j] + 1 > len[i]) len[i] = len[j] + 1, count[i] = count[j];
                    else if (len[j] + 1 == len[i]) count[i] += count[j];
                }
            }
            if (retval == len[i]) retcount += count[i];
            else if (retval < len[i]) retval = len[i], retcount = count[i];
        }
        return retcount;
    }
};

4. 最長のペアチェーン

トピックリンク

分析: 状態が特定の位置で終了することを示している場合、後続の要素は現在のフォーム入力に影響を与えることはできませんが、このトピックはすでに入力に影響を与えているため、すべての配列をソートする必要があります。

  1. ステータス表示

    dp[i]表示以 i 位置为结尾的所有数对链当中,最长的数对链的长度

  2. 状態遷移方程式

    wlx9ovlysc-1690791040047.png

  3. 初期化

    すべて1に初期化される

  4. フォームに記入

    右から右へ

  5. 戻り値

    テーブル全体の最大値を返します。

ACコード:

class Solution 
{
    
    
public:
    int findLongestChain(vector<vector<int>>& pairs) 
    {
    
    
        sort(pairs.begin(), pairs.end());
        int n = pairs.size();
        vector<int> dp(n, 1);
        int ret = 1;
        for (int i = 1; i < n; i++)
        {
    
    
            for (int j = 0; j < i; j++)
            {
    
    
                if (pairs[i][0] > pairs[j][1]) 
                {
    
    
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
            ret = max(ret, dp[i]);
        }
        return ret;
    }
};

5. 最長の固定差分サブシーケンス

トピックリンク

  1. ステータス表示

    dp[i]表示到 i 位置时,所有的子序列当中最长的定差子序列的长度

  2. 状態遷移方程式

    iq9hs9xm3z-1690797357059.png

  3. 初期化

    最初の要素に対応する dp 値を 1 に初期化します。

  4. フォームに記入

    左から右へ

  5. 戻り値

    dpテーブル全体の最大値を返します。

ACコード:

class Solution 
{
    
    
public:
    int longestSubsequence(vector<int>& arr, int difference) 
    {
    
    
        unordered_map<int, int> hash;
        hash[arr[0]] = 1;
        int ret = 1;
        for (int i = 1; i < arr.size(); i++)
        {
    
    
            hash[arr[i]] = hash[arr[i] - difference] + 1;
            ret = max(ret, hash[arr[i]]);
        }
        return ret;
    }
};

6. 最長のフィボナッチ部分列の長さ

トピックリンク

  1. ステータス表示

    dp[i][j]表示以 i j 为结尾的所有子序列当中,最长的斐波那契数列的长度

  2. 状態遷移方程式

    uxobxtvs2s-1690810152050.png

    最適化: 検索を容易にするために配列の要素を添字でバインドします

  3. 初期化

  4. フォームに記入

  5. 戻り値

    戻り値は 3 未満である可能性があり、その場合は 0 が返されるはずです

ACコード:

class Solution 
{
    
    
public:
    int lenLongestFibSubseq(vector<int>& arr) 
    {
    
    
        int n = arr.size();
        unordered_map<int, int> hash;
        for (int i = 0; i < n; i++) hash[arr[i]] = i;
        vector<vector<int>> dp(n, vector<int>(n, 2));
        int ret = 2;
        for (int j = 2; j < n; j++) // 固定最后一个位置
        {
    
    
            for (int i = 1; i < j; i++)
            {
    
    
                int a = arr[j] - arr[i];
                if (a < arr[i] && hash.count(a))
                {
    
    
                    dp[i][j] = dp[hash[a]][i] + 1;
                }
                ret = max(ret, dp[i][j]);
            }
        }
        return ret < 3 ? 0 : ret;
    }
};

7. 最長の算術シーケンス

トピックリンク

  1. ステータス表示

    dp[i][j] 表示 以 i j 为结尾的所有子序列当中最长的等差子序列的长度

  2. 状態遷移方程式

    n4g2m6wqhs-1690814489176.png

    最適化: dp 中に最も近い要素の添え字を保存し、i 位置が埋められた後にそれをハッシュ テーブルに埋めます。したがって、最初に最後から2番目の要素を修正してから、最後から2番目の要素を修正する必要があります

  3. 初期化

  4. フォームに記入

  5. 戻り値

    戻り値は dp テーブル全体の最大値です

ACコード:

class Solution 
{
    
    
public:
    int longestArithSeqLength(vector<int>& nums) 
    {
    
    
        unordered_map<int, int> hash;
        int n = nums.size();
        hash[nums[0]] = 0;
        vector<vector<int>> dp(n, vector<int>(n, 2));
        int ret = 2;
        for (int i = 1; i < n; i++)
        {
    
    
            for (int j = i + 1; j < n; j++)
            {
    
    
                int a = 2 * nums[i] - nums[j];
                if (hash.count(a))
                {
    
    
                    dp[i][j] = dp[hash[a]][i] + 1;
                }
                ret = max(ret, dp[i][j]);
            }
            hash[nums[i]] = i;
        }
        return ret;
    }
};

8. 算術シーケンスの除算 || - サブシーケンス

トピックリンク

  1. ステータス表示

    dp[i][j]表示以 i j 为是等差数列的子序列的个数

  2. ステータス表示

    n4g2m6wqhs-1690814489176.png

  3. 初期化

  4. フォームに記入

  5. 戻り値

ACコード:

class Solution 
{
    
    
public:
    int numberOfArithmeticSlices(vector<int>& nums) 
    {
    
    
        int n = nums.size();
        unordered_map<long long, vector<int>> hash;
        for (int i = 0; i < n; i++) hash[nums[i]].push_back(i);
        vector<vector<int>> dp(n, vector<int>(n));
        int sum = 0;
        for (int j = 2; j < n; j++) // 固定倒数第一个
        {
    
    
            for (int i = 1; i < j; i++)
            {
    
    
                long long a = (long long)nums[i] * 2 - nums[j];
                if (hash.count(a))
                {
    
    
                    for (auto k : hash[a])
                    {
    
    
                        if (k < i) dp[i][j] += dp[k][i] + 1;
                        else break;
                    }
                }
                sum += dp[i][j];
            }
        }
        return sum;
    }
};

Guess you like

Origin blog.csdn.net/qq_63474430/article/details/132032856