バックトラッキングアルゴリズムの問題解決のアイデア
振り返る2つの方法
さまざまな問題解決方法を見て、さまざまな考え方を形成します。
最初に結論についてお話ししましょう。問題解決のアイデア1のバックトラック:選択可能な要素ごとに、選択の余地のない2つの戦略を採用し、再帰的に継続することです。私は最近Huahuajiangのビデオを見て、アイデア2を思いつきました。目標を完了するには、nのステージに分けられます。各ステージには異なる選択肢があり、各ステージで異なる選択肢を試してください。
説明のための例として、特定のleetcode39を取り上げましょう。
タイトル説明
入力:繰り返される要素のない候補の配列、intターゲット
出力:候補の要素を使用してターゲットを形成し、組み合わせの数を出力します。出力結果を繰り返すことはできません。
規則:候補の要素は複数回使用できます。
たとえば、candidates = [2,3,6,7] target = 7、return
[
[7]、
[ 2、2、3 ]
]
アイデア1に従って解決する
結果セットには、List <List>結果で表される複数のソリューションが含まれています。各ソリューションはリストリストです。
候補配列候補の各要素について、それを使用する場合と使用しない場合があります。
たとえば、index = 0の場合、candidates [0]を使用する場合と使用しない場合があります。
候補[0]を使用せず、ターゲットを変更せず、リストを変更せず、インデックス+ 1にして、候補を選択する段階に入ります[1]。
候補[0]を使用すると、ターゲットが小さくなり、候補[0]とインデックス+ 1をリストに追加して、候補[1]を選択する段階に入ります。タイトルには要素を再利用できると書かれているため、candidates [0]は最大でtarget / Candidates [0]回使用できることに注意してください。
コード内のパスを使用して、各要素が選択された回数を記録します。path [0] = Candidates [0]選択の数。
終了条件:target = 0の場合、問題は解決され、結果セットが追加されます。添え字が範囲外の場合、またはtarget = 0の場合は、ループを終了します。
private List<List<Integer>> result = new ArrayList<List<Integer>>();
private int[] path;
private int[] candidates;
/**
* path[i] = 使用nums[i]元素的次数
* @param candidates
* @param target
* @return
*/
public List<List<Integer>> combinationSumV2(int[] candidates, int target) {
result.clear();
path = new int[candidates.length];
this.candidates = candidates;
dfsV2(0, target);
return result;
}
private void dfsV2(int idx, int target) {
if (target == 0) {
List<Integer> solution = new ArrayList<Integer>();
for (int i = 0; i < path.length; i++) {
for (int j = 1; j <= path[i]; j++) {
solution.add(candidates[i]);
}
}
result.add(solution);
return;
}
if (idx >= candidates.length || target < 0) {
return;
}
int maxCount = target / candidates[idx];
for (int i = 0; i <= maxCount; i++) {
path[idx] = i;
dfsV2( idx + 1, target - i * candidates[idx]);
path[idx] = 0;
}
}
アイデア2に従って解決する
target = 7の場合、2、3、6、7の任意の要素、つまり、0から4までの添え字を持つ候補内の任意の要素を選択できます。2を選択し、リストに2を追加して、状態target = 5を入力します。
target = 5の場合、2、3を選択できます。6、7は5より大きいため、剪定を選択する必要はありません。2を選択し、リストに2を追加して、target = 3と入力します。
target = 3の場合、2,3を選択できます。6、7は3より大きいため、剪定を選択する必要はありません。2を選択し、リストに2を追加して、target = 1と入力します。
…
終了条件:target = 0の場合、問題は解決され、結果セットが追加されます。
このアイデアに従って再帰ツリーを描画し、重複するソリューションがあることを確認します。添え字の開始点を制限することで解決できます。たとえば、図で3を選択した後、選択できる要素は3、6、および7の間です。これは、3、6、および7がすべて2より大きいため、再帰を続行する必要がないためです。したがって、再帰ツリーの状態は、開始添え字で追加する必要があります。再帰ツリーを変更します。
public List<List<Integer>> combinationSumV3(int[] candidates, int target) {
result.clear();
Arrays.sort(candidates);
this.candidates = candidates;
if(candidates==null || candidates.length==0) return result;
dfsV3(0,target,new ArrayList<Integer>());
return result;
}
/**
* 在当前状态下,可以选择从start到数组结尾的任意一个元素到结果集
*
* @param start
* @param target
* @param list
*/
private void dfsV3(int start, int target,ArrayList<Integer> list) {
if(target == 0) {
//注意结果需要完全拷贝
result.add(new ArrayList<Integer>(list));
return;
}
if(start>= candidates.length) return;
for(int i = start;i<candidates.length;i++){
if(candidates[i] > target) break;
list.add(candidates[i]);
dfsV3(i,target-candidates[i],list);
list.remove(list.size() - 1);
}
}
毎回dfsは木の高さです。