最近有一场机试,已经说了重点考察dfs,但是对dfs还不是很熟,所以借由学习dfs来输出笔记,从而加深印象。
dfs,深度搜索算法,又可以认为是回溯算法,它其实就是一个决策树的遍历问题,遍历出所有情况,但是先深度后广度。用另外一个名称说这个问题其实就是穷举,穷举能做出的所有选择。
那么其实就是去思考三个问题:
最经典的问题当属全排列问题,如何排列出1-n所有数字,按字典顺序。拿acwing y神的一张图就是这样子写草稿:
那么我们可以看出要分别去做不同的选择最终得到所有结果,光讲可能抽象了点,我们看道题目
给定一个整数?n,将数字?1~n?排成一排,将会有很多种排列方法。
现在,请你按照字典序将所有的排列方法输出。
共一行,包含一个整数?n。
按字典序输出所有排列方案,每个方案占一行。
1≤n≤7
3
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
那么对于这道题结合上面那张图我们就知道首先要从1出发,然后去遍历除了1的1-n其他数字,那么1就要有个状态,记录已经遍历过该数字了,1就要添加到路径之中。
后面去遍历其他数字,其他数字如果也遍历了也就是说在路径里面那么其实就没有必要去遍历了。因为是要全搜索,所以最后我们也一定要把原先遍历的去除掉,把状态删掉
import java.util.*;
public class Main {
static int n;
static int[] path;
static boolean[] state;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
n = in.nextInt();
path = new int[n + 1];
state = new boolean[n + 1];
dfs(1);
}
public static void dfs(int u) {
if(u > n) {
for(int i = 1;i <= n;i ++) {
System.out.print(path[i] + " ");
}
System.out.println();
return;
}
for(int i = 1;i <= n;i ++) {
if(!state[i]) {
path[u] = i;
state[i] = true;
dfs(u + 1);
state[i] = false;
}
}
}
}
总结出一个框架
result = [];
void dfs(路径, 选择列表) {
if(满足结束条件):
result.addd(路径);
return;
for 选择 in 选择列表 {
做选择
dfs(路径,选择列表)
去除此选择
}
}
这个图解思路我觉得写的很好,解答了为什么dg和udg要那么设置函数
import java.util.*;
public class Main {
static int n;
static char[][] chs; // 答案
static boolean[] col, dg, udg; // 状态
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
n = in.nextInt();
chs = new char[n + 1][n + 1];
col = new boolean[n + 10];
dg = new boolean[n + 10];
udg = new boolean[n + 10];
for(int i = 0;i < n;i ++) {
for(int j = 0;j < n;j ++) {
chs[i][j] = '.';
}
}
dfs(0);
}
public static void dfs(int x) {
if(x == n) {
for(int i = 0;i < n;i ++) {
for(int j = 0;j < n;j ++) {
System.out.print(chs[i][j]);
}
System.out.println();
}
System.out.println();
return;
}
for(int y = 0;y < n;y ++) {
if(!col[y] && !dg[y - x + n] && !udg[y + x]) {
chs[x][y] = 'Q';
col[y] = dg[y - x + n] = udg[y + x] = true;
dfs(x + 1);
chs[x][y] = '.';
col[y] = dg[y - x + n] = udg[y + x] = false;
}
}
}
}