Nクイーンの問題|バックトラッキング:N順列

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

上の図は、8クイーンの問題の解決策を示しています。

整数nを指定すると、すべての異なるnクイーン問題の解を返します。

各ソリューションには、n-クイーン問題の明確なポーン配置計画が含まれています。ここで、「Q」と「。」はそれぞれクイーンと空の位置を表します。

例:

入力:4
出力:[
 ["
  .Q .."、//ソリューション1 "... Q"、
  "Q ..."、
  "..Q。"]、

 ["..Q。"、//解法2
  "Q ..."、
  "... Q"、
  ".Q .."]
]


説明:4クイーンの問題には2つの異なる解決策があります。

leetcodeリンク:https ://leetcode-cn.com/problems/n-queens/


1.一般的な考え方

        n人の女王を配置できる各状況をそれぞれ横断し、質問の条件を満たす状況を記録します。トラバーサルが含まれるため、バックトラック方式を検討できます。トラバーサルの順序を並べ替えます。最初に、バックトラッキングレベルiの数に対応するi番目の行を指定し、トラバーサルの各レベルで行jをトラバースします(jが選択されておらず、競合が発生しない限り)。

pos [i]を         使用してi番目の行の列数記録します。つまり、(i、pos [i])にクイーンを配置します。

vis [j]を         使用して、j番目の列が選択されているかどうか記録します。

        書くためのチェック()関数を 選択された位置と選択された位置と競合するかどうかを決定します。

        結果はベクターに保存され、ansという名前が付けられています。この状況は、各バックトラックの最後にあるansに配置する必要があります。シチュエーションはvector <string>の形式にソートする必要があり、print()関数実行ます 


第二に、バックトラッキングアルゴリズムの実現

/* 对于一共有 n 行的棋盘,我们对第 i (0:n-1)行做选择*/
void backtrack(int i, int n) {
    /* 回溯的终点:所有行已经选择完毕 */
    if (i == n) {
        print(n);
        return;
    }
    /* 依次遍历每一列 */
    for (int j = 0; j < n; j++) {
        /* 排除已经被选择 或 会产生冲突的情况 */
        if (vis[j] || !check(i, j))
            continue;

        vis[j] = true;  //列被选择需要做标记
        pos[i] = j;  //记录第 i 行选择了第 j 列
        backtrack(i + 1, n);  //继续进行对下一列的选择
        vis[j] = false;
    }
}

3、print()関数の実現

         特別なことは何もありません。pos配列レコードの対応する実装を使用してください。

/* 对于已经到回溯终点的情况
 * 我们将记录的结果处理为题目需要的图形储存 */
void print(int n) {
    vector<string> temp; //记录此种情况下完整的答案
    /* 依次构建 n 行 */
    for (int i = 0; i < n; i++) {
        string s;
        /* 依次存储每行的 n 个元素 */
        for (int j = 0; j < n; j++) {
            if (j == pos[i])
                s.push_back('Q');
            else
                s.push_back('.');
        }
        temp.push_back(s);  //将此行存入答案
    }
    ans.push_back(temp);  //将此种情况存入总的答案
}

 第四に、check()関数の実現

         現在選択されているポイント(i、j)が前のi行と競合するかどうかを判断するために、バックトラックの順序は行方向であり、列の選択はvis配列による繰り返し選択を防ぐためにも使用されるため、同じ列が同じである場合はありません。それが斜めの側にあるかどうかを判断し、前のi-1の選択を順番にトラバースして、45度の斜めの側にあるかどうかを確認するだけです。絶対値を追加することを忘れないでください〜

bool check(int i, int j) {
    for (int t = 0; t < i; t++)
        if (abs(i - t) == abs(j - pos[t]))
            return false;
    return true;
}

5、補足と分析 

       上記選択的バックトラッキングの実装であると言えますが、n-クイーン問題はソートされたバックトラッキングを使用して実装することもできます。n行の対応する列の選択は相互に排他的であり、その列の選択はすべての並べ替え状況に対応し、競合を除外できます。

完全なコード:

#include <string>
#include <algorithm>
#include <vector>

using namespace std;

class Solution {
public:
    bool vis[100] = {false};
    int pos[100] = {0};
    vector<vector<string>> ans;

    vector<vector<string>> solveNQueens(int n) {
        backtrack(0, n);
        return ans;
    }

    void backtrack(int i, int n) {
        if(i == n) {
            print(n);
            return;
        }

        for(int j = 0; j < n; j++) {
            if(vis[j] || !check(i, j))
                continue;

            vis[j] = true;
            pos[i] = j;
            backtrack(i + 1, n);
            vis[j] = false;
        }
    }

    bool check(int i, int j) {
        for(int t = 0; t < i; t++)
            if(abs(i - t) == abs(j - pos[t]))
                return false;
        return true;
    }

    void print(int n) {
        vector<string> temp;
        for(int i = 0; i < n; i++) {
            string s;
            for(int j = 0; j < n; j++) {
                if(j == pos[i])
                    s.push_back('Q');
                else
                    s.push_back('.');
            }
            temp.push_back(s);
        }
        ans.push_back(temp);
    }
};

 

おすすめ

転載: blog.csdn.net/weixin_43787043/article/details/106064598