回溯算法

 

什么是回溯法?

回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

 

用回溯算法解决问题的一般步骤:

 

1、 针对所给问题,定义问题的解空间,它至少包含问题的一个(最优)解。
 
2 、确定易于搜索的解空间结构,使得能用回溯法方便地搜索整个解空间 。
 
3 、以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。
 

 

基本思想

从一条路往前走,能进则进,不能进则退回来,换一条路再试。

 

能解决哪些问题?

  • 排列、组合(子集、幂集、字符全排列)。
  • 二维数组下的DFS搜索(黄金矿工、数独、装载问题、0-1背包问题、旅行售货员问题、八皇后问题、迷宫问题、图的m着色问题

  • 数组、字符串,给定一个特定的规则,尝试搜索迭代找到某个解。

 

回溯的解空间

回溯算法有两种常见的解空间模型,分别对应数学中的两种暴力思想,组合以及排列。其中子集问题,对应数学中的组合问题。排列问题对应数学中的排列问题。

 

解数独

数独,是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复。

数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”

 

 

利用回溯算法解数独。思路如下:

1,从0行0列开始,依次往里面填入1-9的数字;


2,然后判断填入这个数字后,该数字对应的行,列和九宫格是否满足不重复的条件;


3,如果当前填入的数字满足,就继续填入下一个数字,下一个数字又从1-9中尝试,如果有满足的数字就说明该数字可用,如果没有,则将该数字又重新置为空格,如此循环往复;


4,如果将所有的数字都尝试了还是没有合适的额,那说明本问题无解,返回false;

 1 /*
 2  *java 递归
 3  */
 4 public class ShuduPro {
 5     private int[][] sudoku;
 6     public ShuduPro(int[][] sudoku) {
 7         this.sudoku = sudoku;
 8     }
 9     public static void main(String[] args) {
10         int[][] sudoku={
11                 {7, 0, 0, 0, 0, 0, 0, 0, 9},
12                 {1, 4, 0, 3, 0, 9, 0, 0, 6},
13                 {0, 0, 0, 0, 0, 0, 5, 1, 0},
14                 {0, 0, 0, 0, 0, 0, 0, 0, 0},
15                 {0, 0, 2, 0, 8, 1, 0, 0, 0},
16                 {0, 0, 9, 0, 2, 0, 8, 0, 0},
17                 {0, 0, 0, 0, 0, 0, 0, 0, 0},
18                 {0, 2, 8, 5, 0, 6, 9, 0, 0},
19                 {0, 0, 0, 1, 0, 0, 3, 0, 2}
20         };
21         ShuduPro sudu = new ShuduPro(sudoku);
22         sudu.backTrace(0,0);
23     }
24     private void backTrace(int i, int j) {
25         //完成,打印数组
26         if(i==8 && j==9){
27             print_sudoku();
28             return;
29         }
30         //判断是否到列尾,到列尾没到行尾,就换行
31         if(j == 9){
32             i++;
33             j=0;
34         }
35 
36         //如果是空格就填值
37         if (sudoku[i][j] == 0){
38             for (int n = 1; n <=9; n++){
39                 //判断空格中填任一个数是否符合规则
40                 if(check_repeat(i,j,n)){
41                     /*
42                      *赋值
43                      * 进入下一个空格
44                      * 初始化该空格
45                      */
46                     sudoku[i][j] = n;
47                     backTrace(i,j+1);
48                     sudoku[i][j]=0;
49                 }
50             }
51         }else{
52             backTrace(i,j+1);
53         }
54     }
55 
56     /**
57      *
58      * @param row   行号
59      * @param col   列号
60      * @param temp   赋的值
61      * @return
62      */
63     private boolean check_repeat(int row, int col, int temp) {
64         //判断所在行和列是否有重复数字
65         for (int i=0; i < 9; i++){
66             if(sudoku[row][i] == temp || sudoku[i][col] == temp){
67                 return false;
68             }
69         }
70         //判断所在小九宫格中是否有重复
71         int tempRow = (row / 3) * 3;
72         int tempCol = (col / 3) * 3;
73         for (int i = 0; i < 3; i++){
74             for (int j = 0; j < 3; j++){
75                 if(sudoku[tempRow + i][tempCol + j] == temp){
76                     return false;
77                 }
78             }
79         }
80         return true;
81     }
82 
83     //打印矩阵
84     private void print_sudoku() {
85         for (int i = 0; i < 9; i++){
86             for (int j = 0; j < 9; j++){
87                 System.out.print(sudoku[i][j]+" ");
88             }
89             System.out.println(" ");
90         }
91         System.out.println(" ");
92     }
93 
94 }

 

 

 

解数独结果