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
95 changes: 88 additions & 7 deletions src/bin/step1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,107 @@
// 正解したら終わり

/*
問題の理解
- 家毎に置いてある金額を表す配列numsが与えられる。家から盗める金額の最大値を答えとして返す。
制約として隣接する家からは金を盗めない。
家は円形に並んでいるのでnums[0],nums[nums.len() - 1]は隣接していることになる。

何がわからなかったか
-
- 家が円形だとnums[0]とnums[nums.len() - 1]が隣接しているのでこのケースをどうやって検知して、除外すればよいか分からなかった。

何を考えて解いていたか
-
- まず問題の制約内で考えられるエッジケースを含んだテストケースを考える。
- nums.len()==3のときある家から盗むと他のすべての家は隣接しているので最大値を返せば良い
- 直前の問題と同じ解き方をすると何が問題になるか。
- nums[i - 1].max(nums[i] + nums[i - 2])
累積和の情報しか引き継がないので、その累積和に含まれる金がもともとどの家にあったのかの情報が失われている。
時間切れなので解答を見る。

想定ユースケース
-
解法の理解
- 他の人の解答を見て理解する
https://github.com/nanae772/leetcode-arai60/pull/35/files
https://github.com/h1rosaka/arai60/pull/38/files
https://github.com/docto-rin/leetcode/pull/41/files
以下の考え方で解けると理解した。
- 最初の家nums[0]から探索するときは最後の家nums[nums.len() - 1]は探索対象から外す。
- 最初の家nums[0]を探索しないときは最後の家nums[nums.len() - 1]は探索対象に含める。
実装してみる。

正解してから気づいたこと
-
- 解法を見ると問題をいかにシンプルに捉えられるかがポイントだったと思った。
解法をみて自然に思いついた実装以外(空間計算量O(1))も練習のために実装する。step1a.rs
*/

use house_robber::Robber;

/*
Solution::rob内でRobber::robとできないようスコープ切るためにmodを利用している。
*/
mod house_robber {
use std::collections::HashMap;
pub struct Robber {}

impl Robber {
pub fn collect_max_amount(targets: &[i32]) -> i32 {
let mut amount_cache: HashMap<isize, i32> = HashMap::new();
Self::rob((targets.len() - 1) as isize, targets, &mut amount_cache)
}

fn rob(i: isize, targets: &[i32], amount_cache: &mut HashMap<isize, i32>) -> i32 {
if i < 0 {
return 0;
}

if let Some(amount_cache) = amount_cache.get(&i) {
return *amount_cache;
}

let amount = Self::rob(i - 1, targets, amount_cache)
.max(Self::rob(i - 2, targets, amount_cache) + targets[i as usize]);
amount_cache.insert(i, amount);
amount
}
}
}

pub struct Solution {}
impl Solution {}
impl Solution {
pub fn rob(nums: Vec<i32>) -> i32 {
if nums.is_empty() {
return 0;
}

match nums.len() {
1 => return nums[0],
2 => return nums[0].max(nums[1]),
3 => return nums[0].max(nums[1]).max(nums[2]),
_ => (),
};

let with_first_targets = &nums[0..nums.len() - 1];
let with_out_first_targets = &nums[1..nums.len()];

Robber::collect_max_amount(with_first_targets)
.max(Robber::collect_max_amount(with_out_first_targets))
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn step1_test() {}
fn step1_test() {
assert_eq!(Solution::rob(vec![1, 2, 3, 1]), 4);
assert_eq!(Solution::rob(vec![2, 3, 2]), 3);
assert_eq!(Solution::rob(vec![1, 2, 3]), 3);
assert_eq!(Solution::rob(vec![6, 2, 3, 4, 6]), 10);
assert_eq!(Solution::rob(vec![6, 6, 3, 4, 6]), 12);
assert_eq!(Solution::rob(vec![6, 6, 3, 4, 6, 7, 8]), 20);

assert_eq!(Solution::rob(vec![1]), 1);
assert_eq!(Solution::rob(vec![2, 1]), 2);

assert_eq!(Solution::rob(vec![]), 0);
}
}
83 changes: 83 additions & 0 deletions src/bin/step1a.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Step1a
// 目的: 別の解法を練習する。空間計算量がO(1)となる解法を実装する。

// 方法
// 5分考えてわからなかったら答えをみる
// 答えを見て理解したと思ったら全部消して答えを隠して書く
// 5分筆が止まったらもう一回みて全部消す
// 正解したら終わり

/*
問題の理解
- 家毎に置いてある金額を表す配列numsが与えられる。家から盗める金額の最大値を答えとして返す。
制約として隣接する家からは金を盗めない。
家は円形に並んでいるのでnums[0],nums[nums.len() - 1]は隣接していることになる。

解法について
- 前回の問題(198.House Robber)のレビューで提案してもらった解法で解いてみた。
存在しない家(nums[-2],nums[-1])の金を0として仮定している部分が考え方として番兵に近い気がする。
https://github.com/t9a-dev/LeetCode_arai60/pull/35#discussion_r2564647801
自分でこの解法を思いつくまでの距離は大分ある気がするものの、何をしているのかは理解できている。

何を考えて解いていたか
以下の二通りのケースでそれぞれの最大値を求めて、最後に大きい方を確認して返す。
- 最初の家[0]を含み最後の家を除く場合(0..nums.len()-1)
- 最初の家[0]を含まず最後の家を含む場合(1..nums.len())

正解してから気づいたこと
- 最大値を求めるループのコードが重複しているのでまとめられる。
- テストコードで検知できたが、nums.len() == 1のケースをチェックしないと想定通り動かない。
コーディング試験で行われるようなホワイトボード上でのコーディングだとテストコードによるテスト実行はできないので、配列の境界周りのロジックは大分注意が必要だと思った。
*/

pub struct Solution {}
impl Solution {
pub fn rob(nums: Vec<i32>) -> i32 {
if nums.is_empty() {
return 0;
};
if nums.len() == 1 {
return nums[0];
}

let mut two_before_max = 0;
let mut one_before_max = 0;
for i in 0..nums.len() - 1 {
let current_max = one_before_max.max(two_before_max + nums[i]);
two_before_max = one_before_max;
one_before_max = current_max;
}
let with_first_max = one_before_max;

two_before_max = 0;
one_before_max = 0;
for i in 1..nums.len() {
let current_max = one_before_max.max(two_before_max + nums[i]);
two_before_max = one_before_max;
one_before_max = current_max;
}
let with_out_first_max = one_before_max;

with_first_max.max(with_out_first_max)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn step1a_test() {
assert_eq!(Solution::rob(vec![1, 2, 3, 1]), 4);
assert_eq!(Solution::rob(vec![2, 3, 2]), 3);
assert_eq!(Solution::rob(vec![1, 2, 3]), 3);
assert_eq!(Solution::rob(vec![6, 2, 3, 4, 6]), 10);
assert_eq!(Solution::rob(vec![6, 6, 3, 4, 6]), 12);
assert_eq!(Solution::rob(vec![6, 6, 3, 4, 6, 7, 8]), 20);

assert_eq!(Solution::rob(vec![1]), 1);
assert_eq!(Solution::rob(vec![2, 1]), 2);

assert_eq!(Solution::rob(vec![]), 0);
}
}
55 changes: 45 additions & 10 deletions src/bin/step2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,61 @@
// 改善する時に考えたこと

/*
講師陣はどのようなコメントを残すだろうか?
-

他の人のコードを読んで考えたこと
-

他の想定ユースケース
-
- Vecのような可変長配列の一部分を別のところへ渡す時にコピーが行われないかはだいぶ気にするようになってきた。
Pythonだとスライスでコピーが発生する仕様らしいので覚えておく必要があると思った。
https://github.com/Kaichi-Irie/leetcode-python/pull/6#discussion_r2171435011
同じ指摘を見つけたのでPythonにおけるスライスの暗黙的なコピーはよくある落とし穴っぽいと思った。
https://github.com/ryosuketc/leetcode_arai60/pull/49#discussion_r2217263665

改善する時に考えたこと
-
- 重複しているループのコードをまとめる。
*/

pub struct Solution {}
impl Solution {}
impl Solution {
pub fn rob(nums: Vec<i32>) -> i32 {
if nums.is_empty() {
return 0;
}
if nums.len() == 1 {
return nums[0];
}
Comment on lines +29 to +34
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

エッジケースの処理、こう書いてみてもいいかもしれません。

match nums.len() {
    0 => return 0,
    1 => return nums[0],
    _ => {}
}


let collect_max_amount = |targets: &[i32]| {
let mut two_before_max = 0;
let mut one_before_max = 0;
for target in targets {
let current_max = one_before_max.max(two_before_max + *target);
two_before_max = one_before_max;
one_before_max = current_max;
}
one_before_max
};

let with_first_max_amount = collect_max_amount(&nums[0..nums.len() - 1]);
let with_out_first_max_amount = collect_max_amount(&nums[1..nums.len()]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

細かいですが、withoutで一語なのでwith_outの部分はつなげたいですね。


with_first_max_amount.max(with_out_first_max_amount)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn step2_test() {}
fn step2_test() {
assert_eq!(Solution::rob(vec![1, 2, 3, 1]), 4);
assert_eq!(Solution::rob(vec![2, 3, 2]), 3);
assert_eq!(Solution::rob(vec![1, 2, 3]), 3);
assert_eq!(Solution::rob(vec![6, 2, 3, 4, 6]), 10);
assert_eq!(Solution::rob(vec![6, 6, 3, 4, 6]), 12);
assert_eq!(Solution::rob(vec![6, 6, 3, 4, 6, 7, 8]), 20);

assert_eq!(Solution::rob(vec![1]), 1);
assert_eq!(Solution::rob(vec![2, 1]), 2);

assert_eq!(Solution::rob(vec![]), 0);
}
}
52 changes: 45 additions & 7 deletions src/bin/step3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,61 @@
// 作れないデータ構造があった場合は別途自作すること

/*
時間計算量:
空間計算量:
n = nums.len()
時間計算量: O(n)
空間計算量: O(1)
*/

/*
1回目: 分秒
2回目: 分秒
3回目: 分秒
1回目: 3分20秒
2回目: 3分55秒
3回目: 3分05秒
*/

pub struct Solution {}
impl Solution {}
impl Solution {
pub fn rob(nums: Vec<i32>) -> i32 {
if nums.is_empty() {
return 0;
}
if nums.len() == 1 {
return nums[0];
}

let collect_max_amount = |targets: &[i32]| {
let mut two_before_max = 0;
let mut one_before_max = 0;
for target in targets {
let current_max = one_before_max.max(two_before_max + *target);
two_before_max = one_before_max;
one_before_max = one_before_max.max(current_max);
}
one_before_max
};

let with_first_max_amount = collect_max_amount(&nums[0..nums.len() - 1]);
let with_out_first_max_amount = collect_max_amount(&nums[1..nums.len()]);

with_first_max_amount.max(with_out_first_max_amount)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn step3_test() {}
fn step3_test() {
assert_eq!(Solution::rob(vec![1, 2, 3, 1]), 4);
assert_eq!(Solution::rob(vec![2, 3, 2]), 3);
assert_eq!(Solution::rob(vec![1, 2, 3]), 3);
assert_eq!(Solution::rob(vec![6, 2, 3, 4, 6]), 10);
assert_eq!(Solution::rob(vec![6, 6, 3, 4, 6]), 12);
assert_eq!(Solution::rob(vec![6, 6, 3, 4, 6, 7, 8]), 20);

assert_eq!(Solution::rob(vec![1]), 1);
assert_eq!(Solution::rob(vec![2, 1]), 2);

assert_eq!(Solution::rob(vec![]), 0);
}
}