[アルゴリズムシリーズ(7)]:バックトラック

目次

1.バックトラック

2、アルゴリズムアプリケーション

51.Nクイーン


1.バックトラック

バックトラッキング(探索とバックトラッキング)は、ヒューリスティック手法とも呼ばれる選択的検索手法であり、選択基準に従って前方に検索して目標を達成します。しかし、特定のステップを探索すると、元の選択が適切でないか、目標が達成されていないことがわかったため、1つのステップに戻ってもう一度選択します。失敗した場合に戻って、もう一度やり直すというこの手法は、バックトラッキング方法、およびバックトラッキング条件を満たす特定の状態のポイントこれは「バックトラックポイント」と呼ばれます。

バックトラッキングの問題を解決することは、実際には決定木のトラバーサルプロセスですあなたは3つの質問について考える必要があるだけです:

1.パス:それは行われた選択です。

2.選択リスト:つまり、現在実行できる選択。

3.終了条件:デシジョンツリーの最下部に到達し、選択できなくなった条件です。

バックトラッキングアルゴリズムの基本的なフレームワーク:

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

二分木のトラバーサルについて話すとき、再帰の前の操作をプレオーダートラバーサルと呼ぶことがよくあり、再帰の最後の操作はポストオーダートラバーサルです。これらの2つのトラバーサルとバックトラッキングアルゴリズムの関係は何ですか?バックトラッキングアルゴリズムテンプレートには、選択の実行選択のキャンセルという2つの重要な操作があることがわかります。選択は現在のノードをパスに追加することなので、事前にトラバースする必要があります。現在のブランチがトラバースされている場合は、前のノードに戻って次のブランチをトラバースし続ける必要があるため、選択をキャンセルする必要があります。これは、後続のトラバースで実現できます。ポストオーダートラバーサルは、再帰的に現在のノードに戻ることによって実行される操作にも相当します。

したがって、プレオーダートラバーサルコードは、特定のノードに入る前の時点で実行され、ポストオーダートラバーサルコードは、特定のノードを出た後の時点で実行されます再帰の前に選択を行い、再帰の直後に選択をキャンセルする限り、各ノードの選択リストとパスを正しく取得できます。

このことから、決定木全体を使い果たすことは避けられないため、バックトラッキングアルゴリズムの時間計算量をO(N!)より低くすることはできないこともわかります。これは、バックトラッキングアルゴリズムの機能でもあります。最適化できるサブ問題が重複している動的計画法とは異なり、バックトラッキングアルゴリズムは純粋にブルートフォースであり、一般に複雑さが高くなります。

2、アルゴリズムアプリケーション

51.Nクイーン

  • タイトル説明

クイーン問題 は、n × n チェス盤にnクイーン を配置し、クイーンが互いに攻撃できないようにする方法を研究します。 

上の写真は、8クイーン問題の解決策を示しています。整数nが与えられた場合、すべての異なるnクイーン問題の解を返します。各ソリューションには、n-クイーン問題の明確なポーン配置計画が含まれています。ここで、「Q」と「。」はそれぞれクイーンと空の位置を表します。

示例:
输入:4
输出:[
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。
  • 問題解決のアイデア

この問題は、基本的に完全な順列の問題に似ています。決定木の各レベルは、チェス盤の各行を表します。各ノードが選択できるのは、行の任意の列にクイーンを配置することです。

  • C ++アルゴリズムの実装
bool isValid(const vector<string> &board,const int &row,const int &col){
    int c=board[0].length();
    int r=board.size();
    for(int i=0;i<r;++i){
        if(board[i][col]=='Q'){
            return false;
        }
    }

    // 检查右上方是否有皇后互相冲突
    for (int i = row - 1, j = col + 1;
            i >= 0 && j < c; i--, j++) {
        if (board[i][j] == 'Q')
            return false;
    }
    // 检查左上方是否有皇后互相冲突
    for (int i = row - 1, j = col - 1;
            i >= 0 && j >= 0; i--, j--) {
        if (board[i][j] == 'Q')
            return false;
    }

    return true;
}


void backtrack(vector<vector<string>> &res,
               vector<string> &board,const int &row){
    if(row==board.size()){
        res.push_back(board);
        return ;
    }

    int n=board[0].length();
    for(int i=0;i<n;++i){
        if(!isValid(board,row,i)){
            continue;
        }

        board[row][i]='Q';
        backtrack(res,board,row+1);
        board[row][i]='.';
    }
}


vector<vector<string>> solveNQueens(int n) {
    vector<vector<string>> res;
    vector<string> board(n, string(n, '.'));
    backtrack(res,board,0);

    return res;
}

参照リンク:

Xiaobaiはあなたを学ぶために連れて行きます-バックトラッキングアルゴリズムXiaobaiCV

詳細なバックトラッキングアルゴリズム

おすすめ

転載: blog.csdn.net/wxplol/article/details/108512444