像迷宫这一类的题目使用到的dfs算法都是比较容易想象出搜索过程中的,而有些问题不是那么容易就能想象出搜索过程的,我们把这类问题称之为抽象形式的dfs。
例1:给定n个整数,要求选出K个数,使得选出来的K个数的和为sum。
对于形如这样问题,并没有很明显的地图让你来进行搜索;但依然可以借助dfs来解决这个问题。
- 对于每一个数,枚举选或者不选两种情况。
- 我们在搜索过程中,用S来记录当前选择的数值总和,k来记录选择的数的个数,deep表示当前正在枚举第几个数是否选择。
- 在第一层dfs的时候,我们可以枚举是否选第一个数,如果选择第一个数则让S加上第一个数且k加1,dfs进入下一层;否则dfs直接进入到下一层。
- 在第二层,对于第二个值做同样的处理,dfs的过程中记录已经选取的数的个数,如果已经选取了k个数,判断S值是否等于sum。对于每一层,我们都有两个选择–选和不选。不同的选择,都会使搜索进入不同的分支继续搜索
借助上面的dfs思想,可以写出如下代码:
#include <iostream>
using namespace std;
int a[40];
int n, k, sum, ans;
//i表示选择第i个数,cnt记录选择的个数,s表示选取数的和
void dfs(int i, int cnt, int s) {
if (i == n) {
if (cnt == k && s == sum) {
ans++;
}
return;
}
dfs(i + 1, cnt, s); //不选该数
dfs(i + 1, cnt + 1, s + a[i]); //选择该数
}
int main() {
cin >> n >> k >> sum;
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
ans = 0;
dfs(0, 0, 0);
cout << ans << endl;
return 0;
}
input:
5 3 9
1 2 3 4 5
output:
2
例2:N皇后问题
N皇后问题是一个经典的问题,在一个N∗N的棋盘上放置N个皇后,每行港后放置一个并使其不能互相攻击(同一行,同一列、同一斜线上的皇后都会自动攻击)
上图就是一个合法的8皇后的解。N皇后问题是指:计算一共有多少种的方法放置N个皇后
这道题也可以借助dfs的思想,即先判断第一行填充哪些位置,然后根据第一行填充的位置将棋盘上不能填充的位置标记(不能同一行、同一列、同一斜线),再填充下一行,每次根据之前状态的填充位置进行填充下一行,直到每一行都有皇后为止。
标记棋盘上不能填充的位置,可以使用下面这种比较巧妙地方法:
//col标记列,x1标记正斜线,x2标记反斜线
int col[10], x1[20], x2[20];
col[i] = x1[r + i] = x2[r - i + n] = true;
//r表示填充第r行,(r, i)表示放置的位置,r + i 表示正斜线,r - i + n表示反斜线
//可以简单的推理一下,每一条正斜线的r + i都相等,反斜线的r - i都相等,为了
//防止负数,加一个n确保其为正数
然后根据上面的思路,可以写出如下代码:
#include <iostream>
using namespace std;
int n;
int ans;
bool col[10], x1[20], x2[20];
bool check(int r, int i) {
return !col[i] && !x1[r + i] && !x2[r - i + n];
}
void dfs(int r) {
if (r == n) {
ans++;
return;
}
for (int i = 0; i < n; ++i) {
if (check(r ,i)) {
col[i] = x1[r + i] = x2[r - i + n] = true;
dfs(r + 1);
col[i] = x1[r + i] = x2[r - i + n] = false;
}
}
}
int main() {
scanf("%d", &n);
dfs(0);
cout << ans << endl;
return 0;
}
input:
4
output:
扫描二维码关注公众号,回复:
13034370 查看本文章
2
input1
8
output2:
92
理解抽象形式的dfs的问题求解,有助于加强对于dfs的理解
理解了n皇后问题的求解可以尝试2n皇后问题的求解:可以考虑这样的思路,将其分解为n皇后问题来求解,再传入一个参数p进行dfs,根据传入的参数p的值来判断放置黑皇后或白皇后(此思路仅供参考)