Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions 53_maximum_subarray/divide_and_conquer.cpp
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});
}
};
64 changes: 64 additions & 0 deletions 53_maximum_subarray/memo.md
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$ なのでやむない。見積もりをできる様にならなければ...
Copy link
Copy Markdown

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)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。今後やってみます。


```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`)。
18 changes: 18 additions & 0 deletions 53_maximum_subarray/step1.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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cumsum という単語は numpy 等ではしばしば見かけると思うのですが、変数名として一般的というほどではないように思います。また、単語から文字を削って変数名とした場合、読み手に取って認知負荷が上がる場合があります。これについては以下のコメントをご参照ください。
hemispherium/LeetCode_Arai60#10 (comment)

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;
}
};
16 changes: 16 additions & 0 deletions 53_maximum_subarray/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <limits>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <limits>
#include <algorithm>

std::max<algorithm>にあるものと思います。
(手元の環境ではこのままでもコンパイルが通りました。詳しくは調べられていないのですが、環境によってはstd::vectorをincludeすると内部でstd::algorithmも入ってくるみたいです。)

#include <vector>
class Solution {
public:
int maxSubArray(std::vector<int>& nums) {
int current = nums[0];
int best = nums[0];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

他の書き方として、nums.front() もありますね。


for (int i = 1; i < nums.size(); i++) {
current = std::max(current + nums[i], nums[i]);
best = std::max(best, current);
}

return best;
}
};
18 changes: 18 additions & 0 deletions 53_maximum_subarray/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;
int min_cumsum = 0;
int max_sum = nums[0];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

potrueさんと同じ指摘になってしまいますが、min_cumsumは"累積"和であることが説明されているのに対して、max_sumはなんの和なのかパッと見わかりづらいなと思いました。max_subarray_sumなどとするとさらに安全かと思います。


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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

いいと思います。個人的にはmax_sumはresultとかmax_cumsum_diffとかにするかもしれません。

}
};