JavaScriptで解く!組み合わせ合計(Combination Sum)の問題とバックトラッキング実装
問題概要
重複のない候補数値(candidates)の集合と、目標値(target)が与えられているとします。
ここで求められるのは、候補数値の中から合計が目標値と一致するような、すべての一意な組み合わせを見つける関数を作成することです。
なお、同じ数値は候補の中から何度でも繰り返し選択できるものとします。
注意点
すべての数値(target を含む)は正の整数であること。
解の集合には、重複した組み合わせを含めてはいけないこと。
例
入力が次の場合を考えてみましょう。
candidates = [2,3,6,7], target = 7,
このときの解は以下のようになります。
[
[7],
[2,2,3]
];7 という単一の候補そのものも解になりますし、2 を二回と 3 を一回使った [2,2,3] も解として有効です。
アプローチ:再帰によるバックトラッキング
この問題は「最良の解」や「解の個数」ではなく、「考えられるすべての結果」を求めるものです。そのため動的計画法(DP)を検討する必要はなく、再帰を用いたバックトラッキングによるアプローチが適しています。
バックトラッキングの流れは以下の通りです。
候補となる数値を1つずつ現在の組み合わせに追加し、残りの合計を減らしながら再帰的に探索を進めます。
残りの合計がちょうど
0になった時点で、その組み合わせを結果リストに保存します。残りの合計が
0未満(負)になった場合は、その経路を打ち切って前の状態へ戻ります。再帰呼び出し時に開始インデックス(startFrom)を渡すことで、順序違いの重複した組み合わせが生成されるのを防いでいます。
コード例
以下が実装コードです。
const recursiveSum = (
candidates,
remainingSum,
finalCombinations = [],
currentCombination = [],
startFrom = 0,
) => {
if (remainingSum < 0) {
return finalCombinations;
}
if (remainingSum === 0) {
finalCombinations.push(currentCombination.slice());
return finalCombinations;
}
for (let candidateIndex = startFrom; candidateIndex < candidates.length; candidateIndex += 1) {
const currentCandidate = candidates[candidateIndex];
currentCombination.push(currentCandidate);
recursiveSum(
candidates,
remainingSum - currentCandidate,
finalCombinations,
currentCombination,
candidateIndex,
);
currentCombination.pop();
}
return finalCombinations;
}
const combinationSum = (candidates, target) => recursiveSum(candidates, target);
console.log(combinationSum([2, 3, 6, 7], 7));ポイントとしては、組み合わせを保存する際に currentCombination.slice() でコピーを作成している点です。これは配列を参照渡しすると、その後の pop() 操作によって保存済みの結果まで書き換えられてしまうためです。
出力結果
コンソールへの出力は以下の通りです。
[ [ 2, 2, 3 ], [ 7 ] ]
このように、再帰とバックトラッキングを組み合わせることで、候補数値の重複使用を許容しつつ、重複のない組み合わせをすべて効率的に列挙できます。
-
【JavaScript】配列内のネストされたオブジェクトの値を合計する方法
この記事では、JavaScriptを使って、配列内にネストされた(入れ子構造の)オブジェクトの値を合計する方法を解説します。サンプルコードでは、JSONデータの複数階層に格納された数値(costNum)を順番に取り出し、その合計をブラウザ上に表示します。 コード例 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=d
-
JavaScriptで長方形内に埋め込まれた正方形の周囲長の合計を求める方法
問題 下図のように、1つの長方形の中に正方形が5つ埋め込まれている状況を考えてみましょう。 このとき、それぞれの正方形の周囲長は次のようになります。 4 + 4 + 8 + 12 + 20 = 48 単位 一見すると複雑な計算が必要そうに思えますが、よく観察すると各正方形の一辺の長さがフィボナッチ数列(1, 1, 2, 3, 5, ...)に従っていることが分かります。つまり、n個の正方形が埋め込まれている場合、周囲長の合計は「最初のn項のフィボナッチ数の総和 × 4」で求められるのです。 そこで本記事では、数値 n を引数として受け取り、n個の正方形が埋め込まれた場合の周囲長の合計を返す