1)のソースをベースにしようとしましたが、跡形もなくなってしまったので、最初から説明します。
まずは、基本的な考え方から説明しましょう。
[2,3,6,7]
内の数字を何回か足し合わせて7
を作るという問題ですが、
それぞれの数字の使用回数の組み合わせがどれだけあるか、すべて書き出してみましょう。
まずは、先頭の2
のみを使う場合を考えると、使用回数は、0回、1回、2回、3回の4パターンあります。
(4回以上だと、目的の数値を超えてしまうので対象外です)
これを以下のように表現してみましょう。
(数値は使用回数、右端のカッコ内が合計)
text
12
2────
30(0)
41(2)
52(4)
63(6)
次に、2
と3
の2つのみを使う場合の使用回数を考えます。
2
のそれぞれの使用回数について、3
がいくつ使えるかを調べると、以下のようになります。
text
12 3
2──────
30┬0(0)
4 ├1(3)
5 └2(6)
61┬0(2)
7 └1(5)
82┬0(4)
9 └1(7)
103─0(6)
同様に7
まで続ければ、最終的に以下のようになります。
text
12 3 6 7
2──────────
30┬0┬0┬0(0)
4 │ │ └1(7)
5 │ └1─0(6)
6 ├1─0─0(3)
7 └2─0─0(6)
81┬0─0─0(2)
9 └1─0─0(5)
102┬0─0─0(4)
11 └1─0─0(7)
123─0─0─0(6)
あとは、合計が7
になるものをとりだせば完了です。
つぎに、これをコードに落とし込んでいきましょう。
まずは、先頭の2
のみの場合は簡単でしょう。(tmps
という変数に結果を集めるものとします)
python
1c = 2
2tmps = []
3
4tmp = []
5while target >= sum(tmp):
6 tmps.append(tmp.copy())
7 tmp.append(c)
次に、2
と3
を使う場合ですが、2
のみの場合の結果それぞれについて、やっていけばよいでしょう。
python
1c = 3
2tmps = []
3
4tmp = []
5while target >= sum(tmp):
6 tmps.append(tmp.copy())
7 tmp.append(c)
8tmp = [2]
9while target >= sum(tmp):
10 tmps.append(tmp.copy())
11 tmp.append(c)
12tmp = [2,2]
13while target >= sum(tmp):
14 tmps.append(tmp.copy())
15 tmp.append(c)
16tmp = [2,2,2]
17while target >= sum(tmp):
18 tmps.append(tmp.copy())
19 tmp.append(c)
前回のtmps
をprev_tmps
という名前で参照できるようにすれば、ループにまとめることができます。
python
1c = 2
2tmps = []
3
4tmp = []
5while target >= sum(tmp):
6 tmps.append(tmp.copy())
7 tmp.append(c)
8prev_tmps = tmps
9
10c = 3
11tmps = []
12
13for tmp in prev_tmps:
14 while target >= sum(tmp):
15 tmps.append(tmp.copy())
16 tmp.append(c)
c = 2
の場合とc = 3
の場合のソースをそろえるため、事前にprev_tmps
に[[]]
を入れておきます。
python
1prev_tmps = [[]]
2
3c = 2
4tmps = []
5
6for tmp in prev_tmps:
7 while target >= sum(tmp):
8 tmps.append(tmp.copy())
9 tmp.append(c)
10prev_tmps = tmps
11
12c = 3
13tmps = []
14
15for tmp in prev_tmps:
16 while target >= sum(tmp):
17 tmps.append(tmp.copy())
18 tmp.append(c)
19prev_tmps = tmps
これで、c
をcandidate
で繰り返す形に持ち込めました。
python
1prev_tmps = [[]]
2for c in candidates:
3 tmps = []
4 for tmp in prev_tmps:
5 while target >= sum(tmp):
6 tmps.append(tmp.copy())
7 tmp.append(c)
8 prev_tmps = tmps
最後に、合計がtarget
のものを収集すれば完成です。
すべてまとめると、以下のようになります。
python
1def combinationSum(candidates, target):
2 prev_tmps = [[]]
3 for c in candidates:
4 tmps = []
5 for tmp in prev_tmps:
6 while target >= sum(tmp):
7 tmps.append(tmp.copy())
8 tmp.append(c)
9 prev_tmps = tmps
10 output = []
11 for tmp in prev_tmps:
12 if sum(tmp) == target:
13 output.append(tmp)
14 return output
target
と等しくなった時点で以降の処理が不要になることを利用すれば、もう少し1)のソースに近くなります。
python
1def combinationSum(candidates, target):
2 prev_tmps = [[]]
3 output = []
4 for c in candidates:
5 tmps = []
6 for tmp in prev_tmps:
7 while target > sum(tmp):
8 tmps.append(tmp.copy())
9 tmp.append(c)
10 if sum(tmp) == target:
11 output.append(tmp)
12 prev_tmps = tmps
13 return output
2)のソースについては、dfs(i, cur, total+candidates[i])
が現在の数字の使用回数を増やす方向、dfs(i+1, cur, total)
が次の数字の処理に移行する方向と考えれば、理解できるのではないでしょうか。
余談ですが、私ならこう書く、というソースを載せておきます。
python
1def combinationSum(candidates, target):
2 if target == 0:
3 return [[]]
4 elif target < 0 or not candidates:
5 return []
6 else:
7 c = candidates[:1]
8 return [c + l for l in combinationSum(candidates, target - c[0])] + combinationSum(candidates[1:], target)
念のため、上記ソースの詳細ログ出力バージョンを載せておきます。
呼び出しが深くなるごとにインデントを増やしているので、多少は追いやすいはずです。
python
1def debug(depth, msg):
2 print(' ' * depth + msg)
3
4def combinationSum(candidates, target, depth = 0):
5 debug(depth, f"def combinationSum({candidates}, {target}):")
6 if target == 0:
7 debug(depth, f"return [[]]")
8 return [[]]
9 elif target < 0 or not candidates:
10 debug(depth, f"return []")
11 return []
12 else:
13 debug(depth, f"c = {candidates}[:1]")
14 c = candidates[:1]
15 debug(depth, f"=> c = {c}")
16 debug(depth, f"ret1 = combinationSum({candidates}, {target} - {c}[0])")
17 ret1 = combinationSum(candidates, target - c[0], depth + 1)
18 debug(depth, f"=> ret1 = {ret1}")
19 debug(depth, f"ret2 = [{c} + l for l in {ret1}]")
20 ret2 = [c + l for l in ret1]
21 debug(depth, f"=> ret2 = {ret2}")
22 debug(depth, f"ret3 = combinationSum({candidates}[1:], {target})")
23 ret3 = combinationSum(candidates[1:], target, depth + 1)
24 debug(depth, f"=> ret3 = {ret3}")
25 debug(depth, f"return {ret2} + {ret3}")
26 return ret2 + ret3
27
28print(combinationSum([1], 2))
出力例
text
1def combinationSum([1], 2):
2c = [1][:1]
3=> c = [1]
4ret1 = combinationSum([1], 2 - [1][0])
5 def combinationSum([1], 1):
6 c = [1][:1]
7 => c = [1]
8 ret1 = combinationSum([1], 1 - [1][0])
9 def combinationSum([1], 0):
10 return [[]]
11 => ret1 = [[]]
12 ret2 = [[1] + l for l in [[]]]
13 => ret2 = [[1]]
14 ret3 = combinationSum([1][1:], 1)
15 def combinationSum([], 1):
16 return []
17 => ret3 = []
18 return [[1]] + []
19=> ret1 = [[1]]
20ret2 = [[1] + l for l in [[1]]]
21=> ret2 = [[1, 1]]
22ret3 = combinationSum([1][1:], 2)
23 def combinationSum([], 2):
24 return []
25=> ret3 = []
26return [[1, 1]] + []
27[[1, 1]]
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。