バックトラッキングアルゴリズムの誘導

バックトラッキングアルゴリズムの問​​題解決のアイデア

振り返る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は木の高さです。

おすすめ

転載: blog.csdn.net/flying_all/article/details/105504060