技術共有

リコウバックトラッキング法

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

バックトラッキングとは何ですか?

特定のノードを検索するときに、現在のノード (およびそのサブノード) が必要なターゲットではないことが判明した場合は、元のノードにフォールバックして検索を続行し、現在のノードの変更された状態を復元します。
2 つの小さなヒントを覚えておいてください。1 つ目はステータスを参照 (&) によって渡すことであり、2 つ目は再帰の完了後にすべてのステータスの変更を変更することです。
バックトラック変更には一般に 2 つの状況があります。1 つは順列や結合などの最後の出力を変更する場合、もう 1 つはアクセス ターゲットを変更する場合です。
たとえば、行列内の文字列を検索することを思い出してください。

46.フルアレンジメント

トピック

重複する数値のない配列が与えられた場合nums、それを返します考えられるすべての順列 。あなたはできる任意の順序で答えを返します。

例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

例 2:

输入:nums = [0,1]
输出:[[0,1],[1,0]]

例 3:

输入:nums = [1]
输出:[[1]]

ヒント:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums内のすべての整数お互いに違う​​​​​​​
答え
ソートされた配列の組み合わせをすべて出力するには、バックトラッキング方法を使用できます。
まず位置を決定し、それを後続の各位置と交換します。変更後、次の場所へ移動します。この一連の手順が完了したら、交換したものを交換する必要があります。それがバックトラッキング手法です。
  1. class Solution {
  2. public:
  3. vector<vector<int>> permute(vector<int>& nums) {
  4. vector<vector<int>> ans;
  5. back(nums,0,ans);
  6. return ans;
  7. }
  8. void back(vector<int>& nums,int n,vector<vector<int>>& ans){
  9. int i;
  10. if(n==nums.size()-1)
  11. ans.push_back(nums);
  12. for(i=n;i<nums.size();i++){
  13. swap(nums[i],nums[n]);
  14. back(nums,n+1,ans);
  15. swap(nums[i],nums[n]);
  16. }
  17. }
  18. };

77. コンビネーション

77. コンビネーション

トピック

2 つの整数が与えられた場合nそしてk、戻り範囲[1, n]あらゆる可能性をk数字の組み合わせ。

押すことができます任意の注文答えを返します。

例 1:

输入:n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

例 2:

输入:n = 1, k = 1
输出:[[1]]

ヒント:

  • 1 <= n <= 20
  • 1 <= k <= n

题解

countを設定し、count==kのときのそれぞれの数を計算し、配列に入れます。これは、順列と組み合わせであった以前のものとは異なります。これは配列の選択に少し似ています。

1 ~ n から開始して、各時間の結果を保存する追加の配列を設定します。 i を割り当てた後、count は動的に変化し、dfs の (i+1, n) から数値が選択されます。バックトレース後、カウントします。

  1. class Solution {
  2. public:
  3. vector<vector<int>> combine(int n, int k) {
  4. vector<vector<int>> ans;
  5. vector<int> c(k,0);
  6. int count;
  7. dfs(n,k,1,ans,c,count);
  8. return ans;
  9. }
  10. void dfs(int n,int k,int level,vector<vector<int>>& ans,vector<int>& c,int& count){
  11. int i;
  12. if(count==k){
  13. ans.push_back(c);
  14. return ;
  15. }
  16. for(i=level;i<=n;i++){
  17. c[count++]=i;
  18. dfs(n,k,i+1,ans,c,count);
  19. --count;
  20. }
  21. }
  22. };

79. ユニット検索

79. 単語検索

トピック

与えられたm x n2D キャラクターグリッドboardそして文字列単語word 。もしwordグリッド内に存在する場合、返されますtrue; それ以外の場合は戻りますfalse 。

単語は、アルファベット順に隣接するセルの文字から形成されなければなりません。「隣接する」セルとは、水平または垂直に隣接するセルのことです。同じセル内の文字を繰り返し使用することはできません。

例 1:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true

例 2:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
输出:true

例 3:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"
输出:false

ヒント:

  • m == board.length
  • n = board[i].length
  • 1 <= m, n <= 6
  • 1 <= word.length <= 15
  • boardそしてword大文字と小文字の英字のみで構成されています

答え

同様のルーチン。

dfs + バックトラッキング メソッドでは、最初に訪問を定義して、同じ場所が複数回訪問されることを防ぐために、各検索中にその場所がマークされているかどうかをマークします。

場所については、境界を越えているかどうかを判断するために境界判断を行う必要があります。次に、その文字が訪問されたかどうか、正常に検索されたかどうか、その位置にある文字が目的の文字と異なるかどうかを判断します。

DFS、周りを探してください。

  1. class Solution {
  2. public:
  3. bool exist(vector<vector<char>>& board, string word) {
  4. if(board.empty())
  5. return false;
  6. int m=board.size(),n=board[0].size();
  7. vector<vector<bool>> visit(m,vector<bool>(n,false));
  8. int i,j;
  9. bool find=false;
  10. for(i=0;i<m;i++){
  11. for(j=0;j<n;j++)
  12. back(i,j,board,word,find,visit,0);
  13. }
  14. return find;
  15. }
  16. void back(int i,int j,vector<vector<char>>& board,string& word,
  17. bool& find,vector<vector<bool>>& visit,int level){
  18. if(i<0||i>=board.size()||j<0||j>=board[0].size())
  19. return ;
  20. if(visit[i][j]||find||board[i][j]!=word[level])
  21. return ;
  22. if(level==word.size()-1){
  23. find=true;
  24. return ;
  25. }
  26. visit[i][j]=true;
  27. back(i+1,j,board,word,find,visit,level+1);
  28. back(i-1,j,board,word,find,visit,level+1);
  29. back(i,j+1,board,word,find,visit,level+1);
  30. back(i,j-1,board,word,find,visit,level+1);
  31. visit[i][j]=false;
  32. }
  33. };

51.N クイーン

51. クイーンN

トピック

チェスのルールによれば、クイーンは同じ行または列、または同じ対角線上にある駒を攻撃できます。

クイーン問題研究されているのは、どのようにするかということですnに置かれた女王n×nチェス盤上でクイーン同士を攻撃できなくなります。

整数を与えてくださいn、すべて異なるものを返します 女王問題の解決策。

各ソリューションには異なるものが含まれていますクイーン問題この計画におけるチェスの駒の配置計画'Q'そして'.'それぞれ女王と空席を表しています。

例 1:

输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。

例 2:

输入:n = 1
输出:[["Q"]]

ヒント:

  • 1 <= n <= 9

题意

N Queen には 3 つのラベル付け関数が必要です。1 つは列、1 つは主対角、もう 1 つは副対角です。

各行には必ずクイーンが配置されるため、行ごとに走査され、最後の行が正常に走査され、クイーンが正常に配置されれば十分です。したがって、行マーキング機能を設定する必要はありません。

現時点では、最初の行から開始して列ごとに移動できます。列が false かどうか、主対角線が false かどうか、および 2 次対角線が false かどうかを判断します。存在する場合は、次の列に移動します。そうでない場合は、位置を Q に設定し、次の行に対して同じ検索を実行します。バックトラッキング方法では、検索後に位置とマーキング機能を忘れずに復元してください。

この対角線と副対角線。座標上に描画できます。1 つは y=x+b、もう 1 つは y=-x+b です。

したがって、b=yx となり、b&gt;0 となるため、n を追加します。もう 1 つは y+x です。

  1. class Solution {
  2. public:
  3. vector<vector<string>> solveNQueens(int n) {
  4. vector<vector<string>> ans;
  5. if(n==0)
  6. return {};
  7. vector<string> board(n,string(n,'.'));
  8. vector<bool> c(n,false),l(2*n-1,false),r(2*n-1,false);
  9. back(n,ans,board,c,l,r,0);
  10. return ans;
  11. }
  12. void back(int n,vector<vector<string>>& ans,vector<string>& board,vector<bool>& c,
  13. vector<bool>& l,vector<bool>& r,int row){
  14. if(row==n){
  15. ans.push_back(board);
  16. return ;
  17. }
  18. int i;
  19. for(i=0;i<n;i++){
  20. if(c[i]||l[row-i+n]||r[row+i]){
  21. continue;
  22. }
  23. board[row][i]='Q';
  24. c[i]=l[row-i+n]=r[row+i]=true;
  25. back(n,ans,board,c,l,r,row+1);
  26. board[row][i]='.';
  27. c[i]=l[row-i+n]=r[row+i]=false;
  28. }
  29. }
  30. };

要約する

バックトラッキング方法では、まず境界条件と終了条件を求めます。一般に、i セットは行数と同じです。

境界条件は通常、境界を越えないことです。

通常はマーキング機能の設定が必要です。次に、行ごとまたは位置ごとにアクセスし、訪問済みタグ関数が true に設定されます。次に、次の行または次の行に進みます (通常は再帰的に)。その後、終了後に上記のマーク機能とボードを復元します (通常は 1 つが答えとして設定されます)。一致する場合は、ans 配列にプッシュバックされます。