1.問題の説明:
候補の配列とターゲット数のターゲットを指定して、数の合計をターゲットにすることができる候補のすべての組み合わせを見つけます。
候補の各番号は、各組み合わせで1回のみ使用できます。
説明:
すべての数値(ターゲット数を含む)は正の整数です。
ソリューションセットに重複した組み合わせを含めることはできません。
例1:
入力:候補= [10,1,2,7,6,1,5]、目標 = 8は、
セットが解決される:
[
[1,7]、
[1、2、5]、
[2]、[6]、
[1、1、6]
]
例2:
入力:候補= [2,5,2,1,2]、ターゲット= 5、
解決されたセット:
[
[1,2,2]、
[5]
]
ソース:LeetCode
リンク:https ://leetcode-cn.com/problems/combination-sum-ii
2.思考分析:
①この質問および39のに非常によく似質問39の、質問中の数字は無制限ピックアップであり、ここでは数字だけであるための試みの多少異なるが、それでも不確実にアプローチして、次のいずれかを選ぶことができます組み合わせなので、dfsを使用して解決する必要があります
②この質問の最初の核心は、現在の数を一度しか受け取れないということです。そのため、数を取るときに、現在の数がまだ目標数以下である場合、次の再帰が次の位置になるはずです。現在の数値を1回だけフェッチできるようにするために、この詳細は比較的扱いやすく、最初に配列をソートする必要があります。これにより、dfsメソッドが呼び出されたときに事前にプルーニングして、現在の数値がターゲットの数値に加算されることがわかります。ソート後、確実の後の数はまだそれより大きいので、直接戻ることができ、前のレイヤーで呼び出された場所に戻ることができます。39の碑文ではブレークでしたが、実際には直接戻るのが最善です
③一度しか取得できない現在の数を処理した後、2番目のコアとなる難点は、生成された結果の重複の問題に対処することです。コードの作成当初は、結果の重複を考慮していませんでした。たとえば、並べ替え後の[10,1,2,7,6,1,5] 8が[1,1,2,5,6,7,10]の場合のみ、このエラーが見つかりました。最後に、重複する1があるため[1、2、5]の繰り返し数を生成します
④最後に、配列のソートはとても簡単で、文字列同士がつなぎ合わされているので、文字列を使って重複排除を設定することを考えました。重複排除、ソートがない場合、この方法で重複排除することはできないため、元のコードの重複の問題に簡単に対処できます。
⑤さらに、以前にカラーボタンの解決策を読んだところ、キューまたはスタックを直接ArrayListに変換できることがわかりました。この方法を学ぶことができ、将来的には使用する可能性があります。
3.コードは次のとおりです。
class Solution {
List<List<Integer>> res = new ArrayList<>();
Set<String> set = new HashSet<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
/*排序的另外一个好处是可以用来去重*/
Arrays.sort(candidates);
/*最后一个参数是用来辅助去重的*/
recursion(candidates, target, 0, 0, new Stack<Integer>(), "");
return res;
}
private void recursion(int[] candidates, int target, int pos, int cur, Stack<Integer> stack, String rec) {
if (cur == target) {
if (!set.contains(rec)){
set.add(rec);
res.add(new ArrayList<>(stack));
}
return;
}
if (pos == candidates.length) return;
for (int i = pos; i < candidates.length; ++i){
if (cur + candidates[i] > target) return;
stack.add(candidates[i]);
recursion(candidates, target, i + 1, cur + candidates[i], stack, rec + candidates[i]);
stack.pop();
}
}
}