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
33 changes: 33 additions & 0 deletions 39.CombinationSum/loop.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> all_combinations;
stack<CombinationAndTargetAndIndex> combination_state;
Copy link

Choose a reason for hiding this comment

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

targetとremainって意味的に違う気がして、こっちの名称はremainですかね?
また、C++のstructの名称の付け方の慣習が自分よくわかってないのですが、3つの要素をただandで繋げるのはデータ定義見ればわかるよ、と思ってしまいがちなのですが、どうなんでしょう、、、?

Copy link
Owner Author

Choose a reason for hiding this comment

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

@nittoco
レビューありがとうございます。

3つの要素をただandで繋げるのはデータ定義見ればわかるよ、と思ってしまいがちなのですが、どうなんでしょう、、、?
慣習とかはないと思いあます🙇‍♂️このデータ構造を表すいい名前が思いつかずとりあえず入っているものを繋げました。

いっそのこと無理にstructを使わず、stackを3つ用いた方が分かりやすいですかね?

Copy link

Choose a reason for hiding this comment

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

うーん難しいですね、stackが3つなのもそれはそれで煩雑そうですし(はっきりしてなくてすみません)


combination_state.push({{}, target, 0});

while (!combination_state.empty()) {
auto [partial_combination, remain, start] = combination_state.top();
combination_state.pop();

if (remain == 0) {
all_combinations.push_back(partial_combination);
continue;
}
if (remain < 0) continue;
for (int i = start; i < candidates.size(); i++) {
vector<int> added_combination = partial_combination;
added_combination.push_back(candidates[i]);
combination_state.push({added_combination, remain - candidates[i], i});
}
}
Comment on lines +18 to +23
Copy link

Choose a reason for hiding this comment

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

インデントずれていますね。

一つ上のぶら下がり文のせいでしょうか。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.gblcuhn8bpdi

Copy link
Owner Author

Choose a reason for hiding this comment

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

@oda
レビューありがとうございます。該当箇所みました。
まさに事故を起こしておりました🙇

またこちらの纏めは60問解き終わってからきちんと目を通そうと思います。

return all_combinations;
}

private:
struct CombinationAndTargetAndIndex {
vector<int> combination;
int remain;
int index;
Copy link

Choose a reason for hiding this comment

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

どういうindexかの情報が個人的には欲しいかもです(appending_indexとか?)

};
};
44 changes: 44 additions & 0 deletions 39.CombinationSum/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
## ステップ1
再帰呼び出しごとに、数字を増やしていきその合計がtargetかどうかを確認する
数字の重複OKなので、再帰呼び出し時のindexで少し詰まった

引数が多いきもするが前2問のような考え方で解けた

## ステップ2
・targetの数字から引いていって、残りが0か0を下回るのかで処理するように変更
こうすればtargetとsumを引数に渡していたのをremainだけに減らすことが可能
参考 leetcodeの回答
・変数名変更
all_candidates => all_combinations
partial_candidate => partial_combination
候補って確かに曖昧でした。
・現時点でも合計がtarget以上なのかどうかを再帰呼び出しする前に移動
 remainに対して candidates[i] が大きすぎたら、それ以降の候補は試す必要がない

・loop.cppを追加
tuppleを使うよりstructを用いた
CombinationAndTargetAndIndexの中に部分組み合わせと残りの数字とインデックスを保持。
構造名がすごく微妙

stackの変数名も微妙

## ステップ3
**3回書き直しやりましょう、といっているのは、不自然なところや負荷の高いところは覚えられないからです。**

## 他の解法
>問題文や関数にあるものを命名に使うといい感じになる
ループで解いている。再帰の方法を形で覚えてしまっているので、再帰からループへの変換をしたら良さそう
https://github.com/nittoco/leetcode/pull/25

>変化のある方を比較演算子の左側に置く方がわかりやすい
リーダブルコードを読み直そう
https://github.com/Mike0121/LeetCode/pull/1/commits/b2f5e21e45fc088fb86bdc1f66cb3f6cd4611027

DPを使って解くことも可能
>先にcandidatesをsortしておけば、total+candidates[i] > target となったときにi以降の要素を見る必要はないのでサボれる。
なるほど再帰呼び出しの前に、targetと比較することで少し枝かりができるのですね。
https://github.com/fhiyo/leetcode/pull/52
https://github.com/Yoshiki-Iwasa/Arai60/pull/57

## Discorなど

30 changes: 30 additions & 0 deletions 39.CombinationSum/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> all_candidates;
vector<int> partial_candidate;
GenerateCombinationToTarget(all_candidates, partial_candidate, candidates, target, 0, 0);
return all_candidates;
}

private:
void GenerateCombinationToTarget(vector<vector<int>>& all_candidates, vector<int>& partial_candidate,
const vector<int>& candidates, int target, int start, int sum) {
if (sum == target) {
all_candidates.push_back(partial_candidate);
return;
}
if (sum > target) {
return;
}
for (int i = start; i < candidates.size(); i++) {
partial_candidate.push_back(candidates[i]);
int partial_sum = 0;
for (int num : partial_candidate) {
partial_sum += num;
}
Comment on lines +22 to +25
Copy link

Choose a reason for hiding this comment

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

これ、sum + candidates[i] ですか?

Copy link
Owner Author

Choose a reason for hiding this comment

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

@oda
sum + candidates[i] を使った方が素直ですね🙇‍♂️
再帰から戻ってきた際に、sum -= candidates[i];をしてsumで動くようにしました。

for (int i = start; i < candidates.size(); i++) {
        partial_candidate.push_back(candidates[i]);
        sum += candidates[i];
        GenerateCombinationToTarget(all_candidates, partial_candidate, candidates, target, i, sum);
        partial_candidate.pop_back();
        sum -= candidates[i];
      }

Copy link

Choose a reason for hiding this comment

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

あら、sum を更新せずに、新しい変数にするか引数に直接和を渡せば、引かなくてもいいのではないでしょうか。

Copy link
Owner Author

Choose a reason for hiding this comment

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

@oda
sumを足したり引いたりする方が、バックトラッキングぽいのかなと感じました。
実際は、回答になるpartial_candidateだけを増やしたり減らしたりすることかと思いますが。

GenerateCombinationToTarget(all_candidates, partial_candidate, candidates, target, i, partial_sum);
partial_candidate.pop_back();
}
}
};
27 changes: 27 additions & 0 deletions 39.CombinationSum/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> all_combinations;
vector<int> partial_combination;
sort(candidates.begin(), candidates.end());
GenerateCombinationToTarget(all_combinations, partial_combination, candidates, target, 0);
return all_combinations;
}

private:
void GenerateCombinationToTarget(vector<vector<int>>& all_combinations, vector<int>& partial_combination,
const vector<int>& candidates, int remain, int start) {
if (remain == 0) {
all_combinations.push_back(partial_combination);
return;
}
for (int i = start; i < candidates.size(); i++) {
if (candidates[i] > remain) {
break;
}
partial_combination.push_back(candidates[i]);
GenerateCombinationToTarget(all_combinations, partial_combination, candidates, remain - candidates[i], i);
partial_combination.pop_back();
}
}
};
27 changes: 27 additions & 0 deletions 39.CombinationSum/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> all_combinations;
vector<int> partial_combination;
sort(candidates.begin(), candidates.end());
GenerateCombinationToTarget(all_combinations, partial_combination, candidates, target, 0);
return all_combinations;
}

private:
void GenerateCombinationToTarget(vector<vector<int>>& all_combinations, vector<int>& partial_combination,
const vector<int>& candidates, int remain, int start) {
if (remain == 0) {
all_combinations.push_back(partial_combination);
return;
}
for (int i = start; i < candidates.size(); i++) {
if (candidates[i] > remain) {
break;
}
partial_combination.push_back(candidates[i]);
GenerateCombinationToTarget(all_combinations, partial_combination, candidates, remain - candidates[i], i);
partial_combination.pop_back();
}
}
};