<《アルゴリズムが美しい>>-(5)-バックトラッキングアルゴリズムのコアフレームワーク(1)

コンテンツ

序文

基本コンセプト

基本的なフレームワーク 

例:完全な配列


序文

このブログは、バックトラッキングアルゴリズムの問​​題解決ルーチンフレームワークからのものです:: labuladongのアルゴリズムチートシート(gitee.io)、ここで研究ノートを作成し、印象を深めるためにみんなと一緒に学びたいと思います。

ビデオバージョン:[labuladong]バックトラッキングアルゴリズムのコアルーチンの詳細な説明_ビープマイル_bilibili

基本コンセプト

バックトラッキングアルゴリズムは、実際には列挙に似た検索試行プロセスです。これは主に、検索試行プロセス中に問題の解決策を見つけることです。解決条件が満たされていないことが判明すると、「バックトラック」して別の試行に戻ります。道。バックトラッキング方式は最適な検索方式であり、目標を達成するために最適な条件に従って前方に検索します。ただし、探索が特定のステップに到達し、元の選択が最適でないか、目標を達成できないことが判明した場合、ステップバックして再選択します。これは「バックトラッキングポイント」と呼ばれます。多くの複雑で大規模な問題は、「普遍的な問題解決方法」として知られているバックトラッキング方法を使用できます。 

基本的なフレームワーク 

1.バックトラッキングアルゴリズムは、ブルートフォース枯渇法の一種です。

2.徹底的なプロセスは、実際にはマルチフォークツリーをトラバースするプロセスです。

3.バックトラッキングアルゴリズムのコードフレームワークは、マルチツリートラバーサルコードフレームワークに似ています

バックトラッキングの特定のノードに立って、3つの質問について考える必要があります。

  • パス:現在行っている選択
  • 選択リスト:現時点で可能な選択
  • 終了条件:つまり、決定木の最下部に到達すると、これ以上選択できなくなります。

フレーム:

result=[]; 

def backtrack(路径,选择列表)

     if 满足结束条件: 

        result.add(路径);

        return 

    for 选择 in 选择列表 

        做选择

        backtrack(路径,选择列表)

        撤销选择
 

上記のコードの核となる部分は、forループで選択を行い、再帰して、選択を元に戻すことです。おそらく、上記の3つの質問についてまだ混乱しているかもしれませんが、よく理解できていません。問題は、慌てる必要はありません。ここで言及し、印象を残してください。次に、完全な順列Nクイーンの2つの古典的なトピックを取り上げ、それらを詳細に分析します。

例:完全な配列

この質問を受けたとき、カプセルについてどう思いますか?まず、{1,2,3}の例を見てみましょう。通常、数値を1に固定し、2桁目は2、最後の桁は3、2桁目は3、最後の桁は3になります。 2を取る。そして最後の2つを徹底的に列挙します...

これは紛らわしいように聞こえるので、理解しやすいようにバックトラックツリーを描画しましょう

 このバックトラッキングツリーによると、ルートから順番にトラバースされます。これは実際にはすべての順列です。このバックトラッキングツリーを決定木と呼ぶこともできます。私たちがこれを言う理由は、私たちが次にどこに行くかを決めるために各ノードに立っているからです。私たちが1に立っている場合、写真を見てください。

 私たちは今2つか3つを選ぶことができます、なぜ私たちは以前に歩いたことがあり、完全な配置は繰り返しを許さないので、1つのカプセルを選ぶことができないのですか?

1はパスで、移動したパスを記録します。2と3は選択リストで、現在選択できるパスを示します終了条件は、トラバーサルツリーの最下部のリーフノードが空で、選択リストが空の場合です。

 私たちが定義する関数は、 backtrack 実際にはポインターのようなものであり、このツリー内を歩き、各ノードの属性を正しく維持します。ツリーの最下部に移動するたびに、その「パス」は完全に配置されます。

実際、これはツリートラバーサルの問題であり、マルチフォークツリーのトラバーサルフレームワークは次のようになります。

void traverse(TreeNode root) {
    for (TreeNode child : root.childern) {
        // 前序遍历需要的操作
        traverse(child);
        // 后序遍历需要的操作
    }
}

 いわゆるプレオーダートラバーサルとポストオーダートラバーサルは、2つの異なる時点でのみ行われるものです。プレオーダートラバーサルは、特定のノードに入る前の時点でのトラバーサルであり、ポストオーダートラバーサルは、その後の時点でのトラバーサルです。特定のノードに入る。トラバース。

 今言ったことを思い出してください。「パス」と「選択」は各ノードの属性です。関数がツリーを上に移動するときにノードの属性を適切に維持するには、次の2つの特別な時点で何かを行う必要があります。

 バックトラックのコアフレームワークを理解しましたか?

for 选择 in 选择列表:
    # 做选择
    将该选择从选择列表移除
    路径.add(选择)
    backtrack(路径, 选择列表)
    # 撤销选择
    路径.remove(选择)
    将该选择再加入选择列表

コード: 

List<List<Integer>> res = new LinkedList<>();

/* 主函数,输入一组不重复的数字,返回它们的全排列 */
List<List<Integer>> permute(int[] nums) {
    // 记录「路径」
    LinkedList<Integer> track = new LinkedList<>();
    // 「路径」中的元素会被标记为 true,避免重复使用
    boolean[] used = new boolean[nums.length];
    
    backtrack(nums, track, used);
    return res;
}

// 路径:记录在 track 中
// 选择列表:nums 中不存在于 track 的那些元素(used[i] 为 false)
// 结束条件:nums 中的元素全都在 track 中出现
void backtrack(int[] nums, LinkedList<Integer> track, boolean[] used) {
    // 触发结束条件
    if (track.size() == nums.length) {
        res.add(new LinkedList(track));
        return;
    }
    
    for (int i = 0; i < nums.length; i++) {
        // 排除不合法的选择
        if (used[i]) {
            // nums[i] 已经在 track 中,跳过
            continue;
        }
        // 做选择
        track.add(nums[i]);
        used[i] = true;
        // 进入下一层决策树
        backtrack(nums, track, used);
        // 取消选择
        track.removeLast();
        used[i] = false;
    }
}

 ブルーブリッジカップのはしごでよく見られるのは、次のコードです

 


#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int ants ;
int a[10] = { 0,1,2,3,4,5,6,7,8,9 };

void f(int k)
{
	if (k == 10)
	{
       //这里一般还会有个判断条件
		return;
	}
//从k位开始的每个字符,都尝试放在K这个位置
	for (int i = k; i < 10; i++)
	{
		{int temp = a[i]; a[i] = a[k]; a[k] = temp; }//把后面的每个数字都换到k位
		f(k + 1);
		{int temp = a[i]; a[i] = a[k]; a[k] = temp; }//回溯
	}
}

int main()
{
	f(0);
	cout << ants << endl;
	return 0;
}

C ++を学んだ友人は、next_permutationを使用して直接実装できることを知っています。

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int ants;
int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
int main()
{
	do {
			ants++;
	} while (next_permutation(a, a + 10));
	//f(0);
	cout << ants << endl;
	return 0;
}

もちろん、この質問は、接頭辞の合計、反復、およびその他の方法で行うこともできます。興味のあるパートナーは、下に行って試してみることができます。これまで、バックトラッキングアルゴリズムのフレームワークを紹介してきました。次の記事では、N個のクイーンを解きます。

 

おすすめ

転載: blog.csdn.net/m0_58367586/article/details/123867947