3つの面接の質問を使用して、動的計画法の基本的なロジックを理解します(アイロンが熱いうちに打つ、2番目の質問)

元のタイトルへのリンク:300。最長昇順部分列(LIS)

最初のものを征服するには、ここをクリックしてください!
タイトル説明:

整数配列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

出典:LeetCode(LeetCode)
リンク:https ://leetcode-cn.com/problems/longest-increasing-subsequence
著作権はLeetCodeが所有しています商用転載の場合は、公認機関にご連絡ください。非商用転載の場合は、出典を明記してください。

質問のアイデア:

1.動的計画法の簡単な紹介

dpと呼ばれる動的計画法(動的計画法)は、最適化問題を解決するための一般的な戦略です。一般的に、動的計画法を使用して問題を解決することは、3つのステップにすぎません。

1.1 dp(i)の意味を定義するなど、状態を定義します(状態は元の問題、サブ問題の解決策です)。

1.2 dp(0)の値を設定するなど、初期状態(境界)を設定します

1.3 dp(i)とdp(i-1)の関係を決定するなど、状態遷移方程式を決定します。

2.これらの3つのステップを使用して、上記の問題を解決する方法を見てみましょう。

2.1 numsの値が{10,2,2,5,1,7,101,18}であると仮定すると、最大増加部分列が{2,5,7,101}および{2,5,7,18}であることは明らかです。長さ4

2.2ステータスを定義する

dp(i)がnums [i]で終わる最長増加部分列の長さであるとすると、それはどういう意味ですか?例えば

nums [0]、10で終わる最長の昇順サブシーケンスは10であるため、dp(0)= 1

nums [1]、2の終わりの最長昇順部分列は2であるため、dp(1)= 1

nums [2]、2の終わりの最長昇順部分列は2であるため、dp(2)= 1

nums [3]、5の終わりの最長昇順サブシーケンスは2、5であるため、dp(3)= dp(1)+ 1 = dp(2)+ 1 = 2、ここでdp(1)およびdp(2 )これは、最初の2と2番目の2を5と組み合わせて、上位の昇順サブシーケンスを形成できることを意味します。

nums [4]、1の終わりで最も長い昇順サブシーケンスは1であるため、dp(4)= 1

nums [5]、7の終わりにある最長の昇順サブシーケンスは2、5、7であるため、dp(5)= dp(3)+ 1 = 3

nums [6]、101の終わりにある最長の昇順サブシーケンスは2、5、7、101であるため、dp(6)= dp(5)+ 1 = 4

nums [7]、18で終わる最長の昇順サブシーケンスは2、5、7、18であるため、dp(7)= dp(5)+ 1 = 4

したがって、スプライシングの最長昇順サブシーケンスになる可能性のある要素は、2つの条件を満たす必要があります。まず、現在の要素は、前の最長昇順サブシーケンスの終了値よりも大きい必要があります。上昇しているため、大きくする必要があります。 、それ以外の場合は接続できません。第二に、スプライシング後の長さが最も長い

2.3状態遷移方程式

トラバースj∈[0、i)

nums [i]> nums [j]、nums [i]をnums [j]の後にスプライスして、dp(j)より長い昇順のサブシーケンスを形成できる場合、長さはdp(j)+ 1なので、dp( j)= max {dp(i)、dp(j)+ 1}

nums [i] <= nums [j]の場合、nums [i]をnums [j]の後にスプライスできない場合は、このトラバーサルをスキップしてください

2.4初期状態

dp(0)= 1、要素が1つしかないため、長さは1のみである必要があります。{3,5,1,10}が表示されないようにするため、すべてのdp(i)はデフォルトで1に初期化されます。 iは2に等しい。要素1である場合、dp(i)が1に初期化されていない場合、コードでは、要素1は間違いなく3と5をスキップし、dp(2)= 0であり、明らかに意味を満たしていません。質問の場合、要素が1つしかないため、その長さは1なので、1に初期化する必要があります。

3.あいまいすぎる場合は、平易な英語で説明します

たとえば、現在、King of Gloryをプレイしています。nums[]配列の要素は、各プレーヤーの戦闘力です。dp(i)では、iで終わるチームを形成する人数です。KingofGloryはチームバトルでは、各チームが交代でキャプテンを選択する必要があります。つまり、チームに参加できるすべてのプレーヤーがキャプテンの強みであり、最高のものに基づいて選択されます。チームに2人いるように、{村のプレーヤー、地方のプレーヤー}、そしてこのチームが人を選ぶとき、それはそうであるに違いありません。たとえば、{2、4、5の強さのチームがある場合、国家サービスからのプレーヤーが参加してキャプテンの位置を占めることが望まれます。 、10}の場合、チームが次のキャプテンを選択すると、強さが10を超えるものだけがチームに参加する資格があり、戦闘力が10未満のプレーヤーは排除されます。最も多くの人が登場します。そのようなチームが私たちに必要な答えです。

ナンセンスな話をしないで、コードに行くだけです。審査員にもっと明確に理解させるために、私のコードは簡潔に書かれていません。コードにたくさんのコメントを追加します。はっきりと書いていない、または間違えた場合は、そのエリアにコメントするか、プライベートメッセージを送ってください

class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums == null || nums.length == 0) return 0;
        int[] dp = new int[nums.length];    //由于我是从0下标开始,所以不用nums.length+1
        int max = dp[0] = 1;    //假设只有一个元素,那么最长度就是1
        for(int i = 1; i < nums.length; i++){
            dp[i] = 1;      //如果出现当前的元素是比之前的每条子序列最末位的数小,说明不能加进去,所以当前的元素的拼成的子序列,只有它本身,也就是长度是1
            for(int j = 0; j < i; j++){     
            //如果出现当前的元素是没有比之前的每条子序列最末位的数大,说明不能加进去,就跳过
                if(nums[i] <= nums[j]) continue;
                //代码执行到这里,说明当前元素是比子序列最末位的数大,所以把这个元素加到末尾
                //所以才有dp[j] + 1
                dp[i] = Math.max(dp[i],dp[j] + 1);
                //取出较大值
                max = Math.max(max,dp[i]);
            }
        }
        //返回长度最长的值
        return max;
    }
}

私はここでそれをすべて見ました、あなたは去る前にいいねをクリックすることを考えませんか?気に入らなければ、私の心はビンビンになります。

おすすめ

転載: blog.csdn.net/qq_45798556/article/details/114880742