回溯法是一种经常被用在深度深度优先搜索(DFS)和广度优先搜索(BFS)的技巧。其本质是:走不通就回头。关键词:恢复现场

例题:八皇后问题

As an example, consider the problem of calculating the number of ways n queens can be placed on an n × n chessboard so that no two queens attack each other. For example, when n = 4, there are two possible solutions:
image.png
The problem can be solved using backtracking by placing queens to the board row by row. More precisely, exactly one queen will be placed on each row so that no queen attacks any of the queens placed before. A solution has been found when all n queens have been placed on the board.

For example, when n = 4, some partial solutions generated by the backtracking algorithm are as follows:
image.png
At the bottom level, the three first configurations are illegal, because the queens attack each other. However, the fourth configuration is valid and it can be extended to a complete solution by placing two more queens to the board. There is only one way to place the two remaining queens.

  1. void search(int y) {
  2. if (y == n) {
  3. count++;
  4. return;
  5. }
  6. for (int x = 0; x < n; x++) {
  7. if (column[x] || diag1[x+y] || diag2[x-y+n-1]) continue;
  8. column[x] = diag1[x+y] = diag2[x-y+n-1] = 1;
  9. search(y+1);
  10. column[x] = diag1[x+y] = diag2[x-y+n-1] = 0;
  11. }
  12. }
  13. search(0); //调用

Let backtracking回溯 - 图3 denote the number of ways to place n queens on an n × n chessboard. The above backtracking algorithm tells us that, for example, backtracking回溯 - 图4。(记住:八皇后问题有92种方案)

  1. // 按行枚举
  2. //(1)反对角线 y=x+b, 截距 b=y−x,因为我们要把 b 当做数组下标,所以 b 不能是负的
  3. // 所以我们 +n,保证是结果是正的
  4. //(2)而对角线 y=−x+b, 截距是 b=y+x,这里截距一定是正的,所以不需要加偏移量
  5. // 反对角线,左上到右下
  6. // 对角线,左下到右上
  7. // 反对角线udg[n−u+i],对角线 dg[u+i]中的下标
  8. // n−u+i,u+i 表示的是截距
  9. #include <iostream>
  10. using namespace std;
  11. const int N = 20;
  12. // bool数组用来判断搜索的下一个位置是否可行
  13. // col列,dg对角线,udg反对角线
  14. // g[N][N]用来存路径
  15. int n;
  16. char g[N][N];
  17. bool col[N], dg[N], udg[N];
  18. void dfs(int u)
  19. {
  20. // u == n 表示已经搜了n行,故输出这条路径
  21. if (u == n)
  22. {
  23. for (int i = 0; i < n; i ++ ) puts(g[i]); // 等价于cout << g[i] << endl;
  24. puts(""); // 换行
  25. return;
  26. }
  27. //对n个位置按行搜索
  28. for (int i = 0; i < n; i ++ )
  29. //剪枝(对于不满足要求的点,不再继续往下搜索)
  30. //udg[n - u + i],+n是为了保证大于0
  31. if (!col[i] && !dg[u + i] && !udg[n - u + i])
  32. {
  33. g[u][i] = 'Q';
  34. col[i] = dg[u + i] = udg[n - u + i] = true;
  35. dfs(u + 1);
  36. // 恢复现场 这步很关键
  37. col[i] = dg[u + i] = udg[n - u + i] = false;
  38. g[u][i] = '.';
  39. }
  40. }
  41. int main()
  42. {
  43. cin >> n;
  44. for (int i = 0; i < n; i ++ )
  45. for (int j = 0; j < n; j ++ )
  46. g[i][j] = '.';
  47. dfs(0);
  48. return 0;
  49. }
  1. // 按每个元素进行枚举
  2. #include <iostream>
  3. using namespace std;
  4. const int N = 20;
  5. // 因为是一个个搜索,所以加了row
  6. int n;
  7. char g[N][N];
  8. bool row[N], col[N], dg[N], udg[N];
  9. // s表示已经放上去的皇后个数
  10. void dfs(int x, int y, int s)
  11. {
  12. // 处理超出边界的情况
  13. if (y == n) y = 0, x ++ ;
  14. // 说明已经放好了n个皇后,表示枚举完 n^2 个了
  15. if (x == n)
  16. {
  17. if (s == n)
  18. {
  19. for (int i = 0; i < n; i ++ ) puts(g[i]);
  20. puts("");
  21. }
  22. return;
  23. }
  24. // 不放皇后 就往下搜下一个位置
  25. dfs(x, y + 1, s);
  26. // 放皇后
  27. if (!row[x] && !col[y] && !dg[x + y] && !udg[x - y + n])
  28. {
  29. g[x][y] = 'Q';
  30. row[x] = col[y] = dg[x + y] = udg[x - y + n] = true;
  31. dfs(x, y + 1, s + 1);
  32. row[x] = col[y] = dg[x + y] = udg[x - y + n] = false;
  33. g[x][y] = '.';
  34. }
  35. }
  36. int main()
  37. {
  38. cin >> n;
  39. for (int i = 0; i < n; i ++ )
  40. for (int j = 0; j < n; j ++ )
  41. g[i][j] = '.';
  42. dfs(0, 0, 0);
  43. return 0;
  44. }

参考https://www.acwing.com/solution/content/2820/