diff --git a/08_Dynamic Programming/300. Longest Increasing Subsequence.md b/08_Dynamic Programming/300. Longest Increasing Subsequence.md new file mode 100644 index 0000000..5bab4f1 --- /dev/null +++ b/08_Dynamic Programming/300. Longest Increasing Subsequence.md @@ -0,0 +1,179 @@ +<問題> +https://leetcode.com/problems/longest-increasing-subsequence/description/ + +# step1 + +5分程度答えを見ずに考えて、手が止まるまでやってみる。 +何も思いつかなければ、答えを見て解く。 +ただし、コードを書くときは答えを見ないこと。 +正解したら一旦OK。 +思考過程もメモしてみる。 + +```c++ +class Solution { +public: + int lengthOfLIS(vector& nums) { + vector length(nums.size(), 1); + for (int i = 0; i < nums.size(); i++ ) { + for (int j = 0; j < i; j++) { + if (nums[j] < nums[i]) { + length[i] = max(length[i], length[j] + 1); + } + } + } + return *max_element(length.begin(), length.end()); + } +}; +``` + +【考えたこと】 +- 計算量:O(N^2) 空間:O(N) +- 漸化式にするとLength(i)=max( Length(k)+1, Length(i) ) (k& nums, + const vector& maxlengths) { + int max_length = 0; + for (int i = 0; i < right; i++) { + if (nums[i] < nums[right]) { + max_length = max(max_length, maxlengths[i]); + } + } + return max_length; + } +public: + int lengthOfLIS(vector& nums) { + vector maxlengths(nums.size(), 1); + for (int i = 0; i < nums.size(); i++ ) { + int max_length = get_maxlength_ended_at_index(i, nums, maxlengths); + maxlengths[i] = max_length + 1; + } + return *max_element(maxlengths.begin(), maxlengths.end()); + } +}; +``` + +- 動的計画法については、以下動画がわかりやすかった. + + 本問のsubproblemは、「要素iまでで連続する要素の最大長さ」 + + https://www.youtube.com/watch?v=OQ5jsbhAv_M + +- 命名方法・関数化 + +> lengthsの意味がパズルになっているからだと思いました。 +> 日本語で長々と書くと、「lengths[i] とは、仮に nums[i] がシーケンスの最後であると仮定した場合に可能な、最も長いシーケンスの長さ」ですよね。 +> まあ、「長さ(複数)」であることには間違いないですが、「長さ」とだけいわれて、ああ「仮に nums[i] がシーケンスの最後であると仮定した場合に可能な、最も長いシーケンスの長さ」ってことね、とならず、それを推測するパズルになっています。 +> これを前提とすると、内側のループは、関数にすることができるはずで、「i よりも左で、nums[i] 未満で終わる最大のシーケンスの長さ」を返させればいいですね + +https://github.com/hayashi-ay/leetcode/pull/27/files#diff-b7fbb0dce1473afc0264185268f1a1ef6d682a3a8c997d43bc8bdd636a66ce4aR177 + +上のコメントを見て、関数化した。引数が多くなっているが大丈夫?? + +get_max_length_of_lis_before_indexは、rightより前の部分配列で、nums[right]よりも小さい値で終わるシーケンスの個数 + +length[i]はiで終わるシーケンスの最大要素数 + +関数の命名方法が難しそう。一旦コメントを記載することにした。 + + + +以下、再帰でも書きました。 + +```c++ +class Solution { +private: + map length; + int get_max_length_of_lis_before_index(const vector& nums, int n) { + if (length.contains(n)) { + return length[n]; + } + int max_length = 1; + for (int i = 0; i < n; i++) { + if (nums[i] < nums[n]) { + max_length = max(get_max_length_of_lis_before_index(nums, i) + 1, max_length); + } + } + length[n] = max_length; + return max_length; + } +public: + int lengthOfLIS(vector& nums) { + int max_length; + for (int i = 0; i < nums.size(); i++) { + max_length = get_max_length_of_lis_before_index(nums, i); + } + return max_length; + } +}; +``` + + +- 0(NlogN)の計算量のアルゴリズム(2分探索)もあるよう。step4で追加したい。 + + https://github.com/sakupan102/arai60-practice/pull/32#pullrequestreview-2081534548 + +- 類題 + +https://discord.com/channels/1084280443945353267/1201211204547383386/1241981541802840228 + +- セグメントツリーでの解き方もあるが、ソフトウェアエンジニアの常識ではないとのこと。 + + +- 参考 + + https://github.com/seal-azarashi/leetcode/pull/28 + + https://github.com/TORUS0818/leetcode/pull/33/files + + https://github.com/Ryotaro25/leetcode_first60/pull/34#discussion_r1743810106 + + + + +# step3 + +今度は、時間を測りながら、もう一回、書きましょう。書いてアクセプトされたら文字消してもう一回書く。これを10分以内に一回もエラーを出さずに書ける状態になるまで続ける。3回続けてそれができたらその問題はOK。 + +実施しました。 + + +```c++ +class Solution { +private: +// rightより前の部分配列で、nums[right]よりも小さい値で終わるシーケンスの個数を返す。 + int get_maxlength_ended_at_index(int right, const vector& nums, + const vector& maxlengths) { + int max_length = 0; + for (int i = 0; i < right; i++) { + if (nums[i] < nums[right]) { + max_length = max(max_length, maxlengths[i]); + } + } + return max_length; + } +public: + int lengthOfLIS(vector& nums) { + vector maxlengths(nums.size(), 1); + for (int i = 0; i < nums.size(); i++ ) { + int max_length = get_maxlength_ended_at_index(i, nums, maxlengths); + maxlengths[i] = max_length + 1; + } + return *max_element(maxlengths.begin(), maxlengths.end()); + } +}; +```