-
Notifications
You must be signed in to change notification settings - Fork 0
53. Maximum Subarray #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 300_longest_increasing_subsequence
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| #include <limits> | ||
| #include <vector> | ||
| class Solution { | ||
| public: | ||
| int maxSubArray(std::vector<int>& nums) { | ||
| return helper(nums, 0, nums.size() - 1); | ||
| } | ||
| private: | ||
| int helper(std::vector<int>& nums, int left, int right) { | ||
| if (left == right) { | ||
| return nums[left]; | ||
| } | ||
|
|
||
| int mid = left + (right - left) / 2; | ||
|
|
||
| int max_left = helper(nums, left, mid); | ||
| int max_right = helper(nums, mid + 1, right); | ||
|
|
||
| // cross | ||
| int sum_left = std::numeric_limits<int>::min(); | ||
| int sum = 0; | ||
| for (int i = mid; i >= left; i--) { | ||
| sum += nums[i]; | ||
| sum_left = std::max(sum_left, sum); | ||
| } | ||
|
|
||
| int sum_right = std::numeric_limits<int>::min(); | ||
| sum = 0; | ||
| for (int i = mid + 1; i <= right; i++) { | ||
| sum += nums[i]; | ||
| sum_right = std::max(sum_right, sum); | ||
| } | ||
|
|
||
| int sum_cross = sum_left + sum_right; | ||
|
|
||
| return std::max({max_left, max_right, sum_cross}); | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| ## Step 1 | ||
|
|
||
| ### 1回目 | ||
|
|
||
| 愚直に二重ループでやろうと試みた。$i$ について、$0 < j < i$ となる要素を `nums[i]` に順番に足していって、都度都度評価する。うまくいかず。 | ||
|
|
||
| - ChatGPTに聞いてみて、メモの0が原因だとわかる。部分配列の和は負になりうるので、初期値を変える必要がある。外側のループの中で `nums[i]` で初期化したらこの部分はうまくいった。 | ||
| - で、TLEになった。 `nums.length <= 10^5` で$O^2$ なのでやむない。見積もりをできる様にならなければ... | ||
|
|
||
| ```cpp | ||
| #include <limits> | ||
| #include <vector> | ||
| class Solution { | ||
| public: | ||
| int maxSubArray(std::vector<int>& nums) { | ||
| std::vector<int> memo_largest_sums(nums.size(), 0); | ||
| int largest_sum = std::numeric_limits<int>::min(); | ||
|
|
||
| for (int i = 0; i < nums.size(); i++) { | ||
| int sum = nums[i]; | ||
| for (int j = i; j >= 0; j--) { | ||
| sum = sum + nums[j]; | ||
| if (sum > memo_largest_sums[i]) { | ||
| memo_largest_sums[i] = sum; | ||
| } | ||
| } | ||
| largest_sum = std::max(largest_sum, memo_largest_sums[i]); | ||
| } | ||
|
|
||
| return largest_sum; | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| ### 2回目 | ||
|
|
||
| どうも他のやり方が思いつかなかった。他の方は累積和を連想するらしいので、それでやってみることに。 | ||
|
|
||
| - [参照1](https://github.com/5103246/LeetCode_Arai60/pull/30/changes/a08eb2fb366cee8fe7a865229a2b2fb184f52646#diff-0bb2a784ed14f7b9f9b0b632ec69ad39e239dc06d04e0e1aa45d48ae5a786655)、[参照2](https://github.com/mamo3gr/arai60/pull/30/changes#diff-c0cbe2fd6d52cd3b47dd01eeadaf1bd1ead8275e34e9faed47163982125c6cfb)。 | ||
|
|
||
| - 現在の累積和からこれまでの最小累積和を引いたものを手元に置いておく。これだと $O(n)$ でいける。 | ||
|
|
||
| - コードは `step1.cpp` に書いた。 | ||
|
|
||
| ## Step 2 | ||
|
|
||
| 色々みてみたが、結局 Step 1 の累積和が一番直感的。ついで Kadane。 | ||
|
|
||
| ### 勉強 | ||
|
|
||
| Kadane's algorithm。`step2.cpp` に書いた。 | ||
|
|
||
| - [参照](https://github.com/fhiyo/leetcode/pull/33/changes)。 | ||
| - `i` で終わる最大部分配列は `i - 1` の最大部分配列に `i` を足したものか、`i` 単体かのどちらか。前者がマイナスならば必ず後者の方が大きくなるので前者は捨てれば良い。 | ||
| - [相続の例え](https://github.com/naoto-iwase/leetcode/pull/37)はわかりやすい。負の遺産は放棄した方がいいよね、という。 | ||
|
|
||
| 他にも、Divide-and-conquer approach というのがある。`divide_and_conquer.cpp` に書いた。 | ||
|
|
||
| - 配列を真ん中で2つに分ける。最大部分和は左側・右側・中央を跨ぐのどれか。 | ||
| - 中央を跨ぐものは、両側に伸ばしていってそれぞれ最大のものの和をとって求める。 | ||
|
|
||
| ## Step 3 | ||
|
|
||
| 累積和で3回書いた(`step3.cpp`)。 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| #include <limits> | ||
| #include <vector> | ||
| class Solution { | ||
| public: | ||
| int maxSubArray(std::vector<int>& nums) { | ||
| int cumsum = 0; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cumsum という単語は numpy 等ではしばしば見かけると思うのですが、変数名として一般的というほどではないように思います。また、単語から文字を削って変数名とした場合、読み手に取って認知負荷が上がる場合があります。これについては以下のコメントをご参照ください。 |
||
| int min_cumsum = 0; | ||
| int max_sum = nums[0]; | ||
|
|
||
| for (auto num : nums) { | ||
| cumsum += num; | ||
| max_sum = std::max(max_sum, cumsum - min_cumsum); | ||
| min_cumsum = std::min(min_cumsum, cumsum); | ||
| } | ||
|
|
||
| return max_sum; | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,16 @@ | ||||||
| #include <limits> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| #include <vector> | ||||||
| class Solution { | ||||||
| public: | ||||||
| int maxSubArray(std::vector<int>& nums) { | ||||||
| int current = nums[0]; | ||||||
| int best = nums[0]; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 他の書き方として、 |
||||||
|
|
||||||
| for (int i = 1; i < nums.size(); i++) { | ||||||
| current = std::max(current + nums[i], nums[i]); | ||||||
| best = std::max(best, current); | ||||||
| } | ||||||
|
|
||||||
| return best; | ||||||
| } | ||||||
| }; | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| #include <limits> | ||
| #include <vector> | ||
| class Solution { | ||
| public: | ||
| int maxSubArray(std::vector<int>& nums) { | ||
| int cumsum = 0; | ||
| int min_cumsum = 0; | ||
| int max_sum = nums[0]; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. potrueさんと同じ指摘になってしまいますが、 |
||
|
|
||
| for (auto num : nums) { | ||
| cumsum += num; | ||
| max_sum = std::max(max_sum, cumsum - min_cumsum); | ||
| min_cumsum = std::min(min_cumsum, cumsum); | ||
| } | ||
|
|
||
| return max_sum; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. いいと思います。個人的にはmax_sumはresultとかmax_cumsum_diffとかにするかもしれません。 |
||
| } | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
O(n2) と書くのが一般的だと思います。
また、時間計算量の見積もりができるようになったら、処理時間の見積もりもできるようになるとよいと思います。処理時間の見積もりについては、以下のコメントをご参照ください。
Yuto729/LeetCode_arai60#16 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ありがとうございます。今後やってみます。