类似于树的先根遍历,对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。
两种实现方式:
1、利用栈实现:
while(栈非空){
弹出栈顶元素,并标记已访问;
遍历栈顶元素的所有未访问过的相邻结点,并入栈;
}
2、利用递归实现:
DFS(n){
if(找到解||走不下去 ){
...
return;}
...
dfs(n+1);
}
例题:
方格填数
如下的10个格子
填入0~9的数字。要求:连续的两个数字不能相邻。 (左右、上下、对角都算相邻)
一共有多少种可能的填数方案?
请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
1580
#include <iostream>
using namespace std;
int flag[5][6] = { 0 }, num[5][6]; //增加一行一列便于后续操作
int count1 = 0;
void dfs(int n);
int main() {
int i, j;
for (i = 0; i < 5; i++)
for (j = 0; j < 6; j++)
num[i][j] = -2;
flag[1][1] = flag[3][4] = 1;//初始化
dfs(0);
cout << count1;
return 0;
}
void dfs(int n) {//n为数字,遍历数字可能在的不同位置,数字在放置到不同的位置时有很明确的限制关系。
if (n>9) {
count1++;
return;
}
int i, j;
for (i = 1; i<4; i++) {
for (j = 1; j<5; j++) {
if (flag[i][j] != 1) {
if (num[i - 1][j] == n + 1 || num[i - 1][j + 1] == n + 1 || num[i - 1][j - 1] == n + 1 || num[i][j - 1] == n + 1 || num[i][j + 1] == n + 1 || num[i + 1][j - 1] == n + 1 || num[i + 1][j] == n + 1 || num[i + 1][j + 1] == n + 1)
continue;
if (num[i - 1][j] == n - 1 || num[i - 1][j + 1] == n - 1 || num[i - 1][j - 1] == n - 1 || num[i][j - 1] == n - 1 || num[i][j + 1] == n - 1 || num[i + 1][j - 1] == n - 1 || num[i + 1][j] == n - 1 || num[i + 1][j + 1] == n - 1)
continue; //左右、上下、对角线条件判断
num[i][j] = n;
flag[i][j] = 1;
dfs(n + 1);
flag[i][j] = 0;
num[i][j] = -2;
}
}
}
}
寒假作业
现在小学的数学题目也不是那么好玩的。
看看这个寒假作业:
□ + □ = □
□ - □ = □
□ × □ = □
□ ÷ □ = □
每个方块代表1~13中的某一个数字,但不能重复。
比如:
6 + 7 = 13
9 - 8 = 1
3 * 4 = 12
10 / 2 = 5
以及:
7 + 6 = 13
9 - 8 = 1
3 * 4 = 12
10 / 2 = 5
就算两种解法。(加法,乘法交换律后算不同的方案)
你一共找到了多少种方案?
请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
64
#include <iostream>
#include <cstring>
int flag[13],num[13],am[13];
int count1=0;
using namespace std;
void dfs(int n);
int main(){
int i,j;
memset(flag,0,sizeof(flag));
memset(am,0,sizeof(am));
for(i=0;i<13;i++)
num[i]=i+1;
dfs(1);
cout<<count1;
return 0;
}
void dfs(int n){//深度搜索每个位置放不同的数,n为位置 位置上放置不同的可使用的数 目标是搜索到最后一个位置 且都刚好满足条件
if(n==4){
if(am[1]+am[2]!=am[3])
return ;
}
if(n==7){
if(am[4]-am[5]!=am[6])
return;
}
if(n==10){
if(am[7]*am[8]!=am[9])
return;
}
if(n==13){
if(am[10]!=am[11]*am[12])
return;
else{
count1++;
return;
}
}
int i=0;
for(i=0;i<13;i++){
if(flag[i]!=1){
am[n]=num[i];
flag[i]=1;
dfs(n+1);
flag[i]=0;
am[n]=0;
}
}
}
当用数字进行dfs时,即类似第一种写法那样,反而效率会极低,因为每次都是到最后n==12时才进行判断,并未进行及时的排除。
#include <iostream>
#include <cstring>
int num[4][3], flag[4][3];
int count1 = 0;
void dfs(int n, int s[12]);
using namespace std;
int main() {
int i, j, k;
memset(num, 0, sizeof(num));
memset(flag, 0, sizeof(flag));
int s[13][12];
for (i = 1; i<14; i++) {
for (j = 0, k = 0; j<12; j++) {
k++;
if (k == i)
k++;
s[i - 1][j] = k;
}
}
for (i = 0; i<13; i++) {
dfs(0, s[i]);
}
cout << count1;
return 0;
}//深度搜索每个数在不同的位置 n为第n个数 不断搜索每个数放在不同的可放置的位置,最后判断是否合题意
void dfs(int n, int s[12]) {
if (n >= 12) {
if (num[0][0] + num[0][1] == num[0][2] && num[1][0] - num[1][1] == num[1][2] && num[2][0] * num[2][1] == num[2][2] && num[3][0] == num[3][2]* num[3][1]) {
count1++;
cout << count1<<endl;
}
return;
}
int i, j;
for (i = 0; i<4; i++) {
for (j = 0; j<3; j++) {
if (flag[i][j] != 1) {
num[i][j] = s[n];
flag[i][j] = 1;
dfs(n + 1, s);
flag[i][j] = 0;
num[i][j] = 0;
}
}
}
}
通过以上例题,可以发现使用不同含义类型的n值进行搜索,效率也不同。但可以确定的是,如果能够提前排除掉不合适的情况,而不做费时无用的搜索,则可以提高效率。因此,在进行dfs时关键是要选取好进行搜索的n值的含义类型,而n值含义的选取的关键又可根据是否能够事先排除掉无用情况,减少无用搜索作为依据。