LeetCode の質問 --- フィボナッチ数列モデル

Gu Dequan:個人のホームページ

個人コラム:「Linux オペレーティング システム」  「C/C++」  「LeedCode Question Writing」

キーボードはダメ、年収は100万!


1. N番目のタボナッチ数

質問リンク:1137。N 番目のタボナッチ数  

質問の説明

タボナッチ数列 Tn は次のように定義されます。

        T0=0、T1=1、T2=1、n>=0の条件では、Tn+3=Tn+Tn+1t+Tn+2

        整数 n を指定すると、n 番目のタボナッチ数 Tn の値を返します。

例 1:

        输入:n=4

        输出:4

説明する:

        T_3=0+1+1=2

        T_4=1+1+2=4

例 2:

        输入:n= 25

        输出:1389537

解決

1. ステータスの表現:

        この質問は、「質問の要件に従って」ステータス表現を直接定義できます。

        dp[i] は、i 番目のタボナッチ数の値を表します。

2. 状態遷移方程式:

        タイトルは非常に思慮深く私たちに伝えました:

             dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]

3.初期化:

        再帰式から、i = 0 および i = 1 の場合、dp[-2] または dp[-1] は有効なデータではないため、dp[i] を推定できないことがわかります。

        したがって、フォームに入力する前に、位置 0、1、および 2 の値を初期化する必要があります。

        質問では、dp[0] = 0、dp[1] = dp[2] = 1 であることがすでにわかります。

4. フォームに記入する順序:

        「左から右へ」で間違いありません。

5.戻り値:

        dp[n]の値を返します。

コード

class Solution {
public:
    int tribonacci(int n) 
    {
        if(n == 0) return 0;
        if(n ==1 || n == 2) return 1;

        vector<int> dp(n + 1);
        dp[0] = 0,dp[1] = dp[2] = 1;
        for(int i = 3;i <= n; i++)
            dp[i] = dp[i-1] + dp[i-2] + dp[i-3];
        return dp[n];
    }
};

最適化ソリューション

class Solution {
public:
    int tribonacci(int n) 
    {
        if(n == 0) return 0;
        if(n ==1 || n == 2) return 1;

        int a = 0, b = 1, c = 1, d = 0;
        for(int i = 3;i <= n; i++)
            {
                d = a + b + c;
                a = b;
                b = c;
                c = d;
            }
        return d;
    }
};

第 2 ステップと第 3 ステップの質問

質問リンク:インタビューの質問 08.01. 3 段階の質問  

質問の説明

       3段階の質問。子供が階段を上っています。階段には r 段があり、子供は一度に 1 段、2 段、または 3 段ずつ上がることができます。子供が何通り階段を上れるかを数えるメソッドを実装します。結果は非常に大きくなる可能性があるため、1000000007 を法とする必要があります。

例 1:

        输入:n=3

        输出:4

説明: 移動方法は 4 つあります

例 2:

        输入:n=5

        输出:13

ヒント:

        n の範囲は [1,1000000] です。

解決

1. ステータス表現

       この質問は、「経験 + 質問要件」に基づいて状態表現を直接定義できます。dp[i] は、位置 i に到達する方法が何通りあるかを意味します。

2. 状態遷移方程式

       I ポジションのステータスの最も近いステップで、状況について話し合います。

       dp[i] が子供が階段の最初のステップを上ることができるすべての方法を表す場合、それは階段を上るすべての方法の合計に等しくなります。

             I. 前のステップ、最初のステップでは、dp [i] += dp [i -1] 

             ii. 2 段階上に進み、dp[i] += dp[i - 2]

             iii. 3 段階上に進み、dp[i] += dp[i - 3]

       要約すると、 dp[i] = dp[i - 1] + dp[i - 2] + dp[i]

       この質問では、結果が大きい可能性があるため、結果をモジュロにする必要があると述べていることに注意してください。

       計算するときは、3 つの値をすべて加算してから係数を取得します (dp[i l+ dp[i - 2] + dp[i - 3])% MOD はお勧めできません。試してみて、最大値が範囲内にある場合、Web サイトはエラー signedinteger オーバーフローを報告します。係数を必要とするこのタイプの問題では、計算 (2 つの数値の加算/乗算など) を行うたびに係数を取得する必要があります。そうしないと、オーバーフローが発生した場合に、私たちの答えは間違ってしまいます。

3.初期化

       漸化式からわかるように、i = 0、i = 1、i = 2 の場合、dpL3]dp[-2] または dp[-1] は有効なデータではないため、dp[i] を推定することはできません。

       したがって、フォームに入力する前に、位置 1、2、および 3 の値を初期化する必要があります。

       質問の意味によれば、dp[1= 1、dp[2] = 2、dp[3] = 4

4. フォームに記入する順序

        「左から右へ」で間違いありません。

5. 戻り値

       dp[n]の値を返します。

コード

class Solution {
public:
    int waysToStep(int n) 
    {
        const int MOD = 1e9 + 7;
        if(n == 1 || n == 2) 
            return n;
        if(n == 3) 
            return 4;
        vector<int> dp(n + 1);
        dp[1] = 1, dp[2] = 2, dp[3] = 4;
        for(int i = 4;i <= n; i++)
            dp[i] = ((dp[i-1] + dp[i-2]) % MOD + dp[i-3]) % MOD;
        return dp[n];
    }
};

最適化ソリューション

class Solution {
public:
    int waysToStep(int n) 
    {
        const int MOD = 1e9 + 7;
        if(n == 1 || n == 2) 
            return n;
        if(n == 3) 
            return 4;
        int a = 1, b = 2, c = 4, d = 0;
        for(int i = 4;i <= n; i++)
        {   
            d = ((a + b) % MOD + c) % MOD;
            a = b;
            b = c;
            c = d;
        }
        return d;
    }
};

3. 最小限のコストで階段を登る

質問リンク: 746. 最小限のコストで階段を上る  

質問の説明

       整数配列のコストを与えます。ここで、cost[i] は、階段の i 番目のステップを上るコストです。この料金を支払うと、1 つまたは 2 つの階段を登ることができます。

       インデックス 0 またはインデックス 1 のステップから階段を登り始めるかを選択できます。階段の一番上に到達するまでの最小コストを計算して返してください。

例 1:

        输入: cost = [10,15,20]

        输出:15

説明する:

       インデックス 1 のステップから開始します

       15 を支払い、2 段の階段を上って最上部に到達すると、合計コストは 15 になります

例 2:

        输入: cost =[1,100,1,1,1,100,1,1,100,1]

        输出:6

説明する:

        インデックス 0 のステップから開始します。

        1 を支払い、2 段を登って 2 とマークされた段に到達します。

        1を支払い、2段登り、添え字4の最終段に到達します。

        1を払って2段登り、最後の マークの段に到達します。

        1を払って一段登り、指数7の段に到達します。

        1 を支払い、階段を 2 つ登り、9 と書かれた段に到達します。

        a a asとして

        合計コストは6です。

注意注意:

     この質問では、配列内の各添字 [o, n - 1] がフロアを表しており、最上階の位置は実際には位置 n にあります。 

解決

1. ステータスの表現:

       この質問は、「経験 + 質問要件」に基づいて状態表現を直接定義できます。

       位置 1 で終わります、何とか何とか何とか

       dp[i] は、位置 1 に到達するための最小コストを意味します。 (注: 位置 i に到着したとき、位置 i のお金を数える必要はありません)

2. 状態遷移方程式:

最新のステップに基づいて、次の状況について説明します。

             まず i -1 の位置に到着し、コスト [i -1] を支払います。

             次に、i 位置にステップを実行します。 dp[i - 1]+ csot[i - 1];

             まず I -2 の位置に到着し、コスト [i -2] を支払います。

             次に、i 位置まで歩きます: DP [i -2]+ csot [i -2]。

3.初期化:

       漸化式から、最初に i = 0 と i の値を 1 に初期化する必要があることがわかります。コストをかけずに 1 層目と 2 層目に直接立つことができるため、dp[0] = dp[1] = 0 を取得するのは簡単です。

4. フォームに記入する順序:

       「状態遷移方程式」によれば、トラバースの順序は「左から右」です。

5.戻り値:

       「状態表現と質問の要件」に従って、dp[n] 位置の値を返す必要があります。

コード

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) 
    {
        int n = cost.size();
        vector<int> dp(n + 1);
        for(int i = 2; i <= n; i++)
            dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2]);
        return dp[n];
    }
};

4. デコード方法

質問リンク:91. デコード方法   

質問の説明

       文字 A-Z を含むメッセージは、次のマッピングを使用してエンコードされます:

        「A」 -> 「1」
        「B」 -> 「2」
        ...
        「Z」 -> 「26」

       エンコードされたメッセージをデコードするには、すべての数値を上記のマッピング方法に基づいて文字にマッピングし直す必要があります (方法は複数ある場合があります)。たとえば、"11106" は次のようにマッピングできます。

  • "AAJF" 、メッセージをグループ化します。 (1 1 10 6)
  • "KJF" 、メッセージをグループ化します。 (11 10 6)

   を  にマッピングできないため、メッセージを (1 11 06) としてグループ化できないことに注意してください。 a i=4> と  はマッピングにおいて同等ではありません。 "06""F""6""06"

       数値のみを含む空ではない文字列が指定された場合 s は、デコード メソッドの合計数を計算して返します。

       質問データは、回答が 32 ビット整数であることを保証します。

例 1:

入力:s = "12"
出力: 2
説明: 「AB」 (1 2) または「L」 (12) としてデコードできます。

例 2:

入力:s = "226"
出力: 3
説明: 「BZ」 (2 26)、「VF」 (22 6)、または「BBF」 (2 2 6) としてデコードできます。 。

例 3:

入力:s = "06"
出力: 0
説明: 「06」は先頭にゼロがあるため「F」にマッピングできません(「6」と「06」は等価ではありません)。 。

ヒント:

  • 1 <= s.length <= 100
  • s 数字のみが含まれ、先頭にゼロが含まれる場合があります。

解決

フィボナッチ数列に似ていますね~

1. ステータスの表現:

       過去の経験に基づいて、ほとんどの線形 dp では、特定の位置で終了または開始することが経験されています。ここでは、状態表現を定義するための「トピック要件」と組み合わせて、「i 位置で終了」を試み続けます。

        dp[i] は、文字列の区間 [0, i] にエンコード方式がいくつあるかを意味します。

2. 状態遷移方程式:

        状態表現を定義した後、位置 i の dp 値を分析し、「前」または「後」の情報からそれを推定する方法を分析できます
。 i 位置のエンコード状況については、次の 2 つの状況に分類できます。

             i. 位置 i の数字を文字のみにデコードします。

             ii. i 位置の数値を i - 1 位置の数値と組み合わせて文字にデコードします。

以下では、上記の 2 つのデコード状況の分析を続けます。

     位置 i の数字が文字のみにデコードされる場合、「デコード成功」と「デコード失敗」の 2 つの状況が発生します。

          i. デコード成功: 位置 i の数値が [1, 9] の間にある場合、位置 1 の数値は独立してデコードできることを意味します。区間 [0, i] のデコード方法は、区間 [0, i - 1] のデコード方法と同じである必要があります。すべてのデコード結果は [0, i - 1] 区間になるため、i の位置にデコードされた文字を入力するだけです。このとき dp[i] = dp[i - 1] ;

          ii. デコード失敗: 位置 i の数値が 0 の場合、位置 1 の数値だけではデコードできないことを意味し、この時点では [0, i]この区間にはデコード方法はありません。なぜなら、位置 1 が単独でデコードに参加してもデコードが失敗した場合、それまでの努力はすべて無駄になるからです。このとき dp[i] = 0 となります。

     位置 1 の数値を位置 i – 1 の数値と組み合わせて文字にデコードします。また、「デコード成功」と「デコード失敗」の 2 つの状況もあります。

          i. デコード成功: 結合された数値が [10, 26] の間にある場合、2 つの位置 ([i - 1, i] が正常にデコードできた) を意味します。の場合、同様の理由で、[0, i] 区間での復号方法と [0, i - 2] 区間での復号方法は同じになるはずです。このとき、 dp[i] = dp[i - 2] となります。 ]; デコード失敗:組み合わせた数値が[0, 9] ~ [27, 99]の場合、次のデコードに失敗したことを意味します。 2 つの位置を組み合わせた場合 (00 01 02 03 04...ここではこれらの状況に注意してください)、[0, i] 区間のデコード方法は現時点では存在せず、理由は同じです。上記と同様。この時点では dp[i] = 0 です。
          ii.

       要約すると、 dp[i] の最終結果は、上記 4 つのケースで成功した 2 つのデコードの累積和になるはずです (デコード方法が心配なので、デコードが失敗したため、それを加算する必要はありません)最終結果)、状態遷移方程式を得ることができます。

       (dp[i]はデフォルトで0に初期化されます) 

          i. s[i] の数値が区間 [1, 9] にある場合: dp[il t= dp[i - 1]

          ii.s[i - 1] が s[i] の数値と結合される場合、[10, 26: dp[i] += dp] の場合[i - 2] ;

       上記 2 つの判断がどちらも当てはまらない場合は、デコード方法が存在しないことを意味し、dp[i] はデフォルト値 0 となります。

3.初期化:

方法 1 (直接初期化):

     i - 1 とその位置の dp 値が使用される可能性があるため、「最初の 2 つの位置」を最初に初期化する必要があります。 dp[o] を初期化します。

         i. s[0] == '0' の場合、エンコード方式はなく、結果は dp[0] = 0;< /span>

         ii.s[0] != '0' の場合、エンコードは成功します (dp[0] = 1)

dp[1j: を初期化します。

         i. s[1] が [1, 9] の間の場合は個別に符号化できます。このとき、 dp[1] += dp[0] (理由は上記と同じで、dp[1] のデフォルトは 0)

         ii.s[0] と s[1] の組み合わせの後の数字が ([10, 26] の間にある場合、最初の 2 文字のうち、再びエンコード方法があり、現時点では dp[1] += 1

方法 2 (補助位置初期化を追加):

     初期化に役立つ補助ノードを先頭に追加できます。この手法を使用する場合、次の 2 つの点に注意してください。

         i.補助ノードの値は、後続のフォーム入力が正しく行われることを保証する必要があります。

         ii.添字マッピング関係

4. フォームに記入する順序:

       「左から右へ」で間違いありません。

5.戻り値:

       区間 [o, n - 1] でのエンコード方式を示す dp[n - 1] の値を返す必要があります。

コードの実装 --- 方法 1

class Solution {
public:
    int numDecodings(string s) 
    {
        int n = s.size();
        vector<int> dp(n);
        dp[0] = s[0] != '0';

        if(n == 1) 
            return dp[0];

        if(s[0] != '0' && s[1] != '0') 
            dp[1] += 1;
        int t = (s[0] - '0') * 10 + s[1] - '0';
        if(t >= 10 && t <= 26) 
            dp[1] += 1;

        for(int i = 2; i <n; i++)
        {
            if(s[i] != '0') 
                dp[i] += dp[i-1];
            int t = (s[i-1] - '0') * 10 + s[i] - '0';
            if(t >= 10 && t <= 26) 
                dp[i] += dp[i-2];
        }
        return dp[n-1];
    }
};

コードの実装---方法 2

class Solution {
public:
    int numDecodings(string s) 
    {
        int n = s.size();
        vector<int> dp(n + 1);
        dp[0] = 1;
        dp[1] = s[1 - 1] != '0';


        for(int i = 2; i <= n; i++)
        {
            if(s[i-1] != '0') 
                dp[i] += dp[i-1];
            int t = (s[i-2] - '0') * 10 + s[i-1] - '0';
            if(t >= 10 && t <= 26) 
                dp[i] += dp[i-2];
        }
        return dp[n];
    }
};

結論:今日の質問共有はこれで終了です。この記事を共有することで、皆さんの学習に少しでも役立つことを願っています。ご質問がございましたら、お気軽にお尋ねください。全員退室してください。コメントエリアにメッセージ~~~ 

おすすめ

転載: blog.csdn.net/m0_71746526/article/details/134701418