Likou300。最長の増分サブシーケンス(動的計画法)

300.最長増加部分列

整数配列numsを指定し、厳密に増加する最長のサブシーケンスの長さを見つけます。

サブシーケンスは、配列から派生したシーケンスであり、残りの要素の順序を変更せずに、配列内の要素を削除します(または削除しません)。たとえば、[3,6,2,7]は配列[0,3,1,6,2,2,7]のサブシーケンスです。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1
 

提示:

1 <= nums.length <= 2500
-104 <= nums[i] <= 104
 

进阶:

你可以设计时间复杂度为 O(n2) 的解决方案吗?
你能将算法的时间复杂度降低到 O(n log(n)) 吗?

回答:

このタイプの問題シーケンスはランダムに組み合わせることができ(つまり、サイズが増加に対応する限り)、このタイプを組み合わせる方法は多数あるため、ループトラバースのみを使用すると解決が困難になります。

したがって、このような一見「複雑な」問題については、動的計画法のアイデア使用して問題を解決することを検討できます。この質問は、Likouの最長の連続サブシーケンスの数に似てます。

dp [i]をi添え字で終わる最長増加部分列として定義するので、動的遷移方程式の1つを見つけます:
dp [i] = max(dp [i]、d [j] +1)
そしてそれはまたそれはi添え字で終わらないので、次のようになります
。Max= max(dp [i]、max)
maxは、先行するすべてのdp [i-1]、dp [i-2]、dp [i- 3]など。最大値を見つけます。

詳細な説明:

1つ目は、問題を分解し、最も長く増加するサブシーケンスの数を要求することです。最後の要素で終わる最も長く増加するサブシーケンスと、最後の要素で終わらない最も長く増加するサブシーケンスに分割して、2つのYouをペアにすることができます。最大を求めることができます。
したがって、同じことがどのような状況にも当てはまります。

dp [i]は、i番目の要素で終わる最長増加部分列として定義できます。これは、i番目の要素で終わるため、多くの場合に分割されます。つまり、前の要素に接続されます。(この質問は任意につなげることができるので)前にi-1があるので、毎回列挙して前の結果と比較してください。

したがって、状態遷移方程式を取得できます
。dp[i] = max(dp [i]、dp [j] +1);
(j <= i-1)
i番目で終了することはできませんが、 i -1、i-2、i-3 ... endなので、各dp [i]を計算した後、前の各dp [i-1]、dp [iと比較する必要があります。 -1]、dp [i-3] ...
そして、上記の操作は面倒なので、変数を使用して最初のいくつかの最大値を記録し、最大値のみをdp [i]と比較します。****

コード1:

int Max(int x,int y)
{
    
    
    return x>y?x:y;
}
int lengthOfLIS(int* nums, int numsSize){
    
    
    int dp[numsSize+1];
    if(numsSize<2)
    {
    
    
        return numsSize;
    }
    for(int i=0;i<numsSize;i++)
    {
    
    
        dp[i] = 1;
    }
    int max = dp[0];
    for(int i=1;i<numsSize;i++)
    {
    
    
        for(int j=0;j<i;j++)
        {
    
    
            if(nums[j]<nums[i])
                dp[i] = Max(dp[i],dp[j]+1);//由于一直进行比较,所以dp[i]本身也在不断更新
        }
        max = Max(max,dp[i]);
    }
    return max; 
}

コード2:

char a[100];memset(a,1,sizeof(a));

int lengthOfLIS(int* nums, int numsSize){
    
    
    /* 1、定义dp数组,存放当前节点的最长递增子序列值 */
    int dp[numsSize];
    for (int i = 0; i < numsSize; i++) {
    
    
        dp[i] = 1;
    }
    /* 2、定义返回值 */
    int res = 1;
    /* 3、外层循环遍历数组中所有节点 */
    for (int i = 0; i < numsSize; i++) {
    
    
        /* 3.1、内层循环从0到当前节点 */
        for (int j = 0; j < i; j++) {
    
    
            /* 计算当前节点的最长递增子序列长度 */
            if (nums[i] > nums[j] && (dp[i] < dp[j] + 1)) {
    
    
                dp[i] = dp[j] + 1;
            }
        }
        /* 3.2、比较节点的最长递增子序列长度赋给res */
        if (res < dp[i]) {
    
    
            res = dp[i];
        }        
    }
    /* 4、返回结果 */
    return res;
}

おすすめ

転載: blog.csdn.net/xiangguang_fight/article/details/115100351