深度优先搜索 DFS

类似于树的先根遍历,对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。

两种实现方式:

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值含义的选取的关键又可根据是否能够事先排除掉无用情况,减少无用搜索作为依据。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值