深度搜索入门

深度优先搜索是搜索的手段之一。它从某个状态开始,不断地转移状态,直到无法转移,然后回退到前一步的状态,继续转移到其他状态,如此重复,直到找到最终的解。

做这类题目,抓住两样东西:1.总体上递归几次(几层)?每一次递归确定一层上的数。 2.每次递归,有几种选择的情况。所以dfs()函数,只有两部分(if、else结构):1.(if部分)若每一层都选择了,判断是否符合条件,做出题目要求的操作。2.(else部分)若还有层没有选择,就做出选择,所有选择的情况列出。

下面是几个考察dfs的题目:

1.部分和问题(入门题)——南阳1282(dfs)

问题描述:http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=1282

题目要求:在n个数中,选出某些,使得它们之和等于k,可以做到,输出YES,不可以做到,输出NO。

分析:有n个数,大体递归n(或n+1)次,分别确定n(或n+1)层的数;每次递归,有两种选择:加这个数、不加这个数。

其状态转移过程如下:

代码<c语言>:

 1 //每个数分取和不取两种情况
 2 #include<stdio.h>
 3 int n,k,arr[21];//设成全局变量,是的dfs函数的参数减少,函数变简洁//dfs函数,用来确定第i层上的数
 4 bool dfs(int i,int sum)
 5 {
 6     if(i==n)return sum==k;
 7 //两种选择
 8     if(dfs(i+1,sum)) return true;//不取这个数,sum+arr[i]表示到第i+1层数的和
 9     if(dfs(i+1,sum+arr[i])) return true;//取这个数
10     return false;
11 }//主函数
12 int main()
13 {
14     int i;
15     while(~scanf("%d %d",&n,&k))
16     {
17         for(i=0; i<n; i++)
18         {
19             scanf("%d",&arr[i]);
20         }
21
22         if(dfs(0,0))//第一层,是0,不是第一个数,所以要n+1层
23             printf("YES\n");
24         else
25             printf("NO\n");
26     }
27     return 0;
28 }

2.部分和问题——南阳1058(dfs+1组记录)

题目描述:http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=1058

题目要求:在题目1的基础上,输出由那些数组成。

分析:同题目1,但是不仅要判断是否可以找出某些数之和正好是k,还要输出这些数,我们在递归的过程中,如果满足条件,就把数存入数组。本题的dfs函数,只比上一题多一行代码:result[j++]=arr[i],存数的过程。

注意:若给出的数,有多组都符合条件,此代码只输出一组。并且,选择的次序不同,会导致结果不同。如:

输入:4 12

4 8 1 7

“先加这个数”:输出:YES

4 1 7

“后加这个数”:输出:YES

4 8

但是,两种写法,虽然结果不同,都AC了。

代码<c语言>:

 1 #include<stdio.h>
 2 int n,k,j=0,arr[21],result[21];//arr[]存储题目给出的数,result[]存储符合条件的数的下标//dfs函数
 3 bool dfs(int i,int sum){
 4 if(i==n)return sum==k;
 5 if(sum>k)return false;
 6 //下面自然是i<n的情况,有两种情况
 7 /*if(dfs(i+1,sum+arr[i])) {//位置不同(先加这个数),结果不同,但是都AC了
 8     result[j++]=arr[i];//记录取出的数据
 9     return true;
10 }*/
11 if(dfs(i+1,sum))
12     return true;
13 if(dfs(i+1,sum+arr[i])) {//位置不同(后加这个数)
14     result[j++]=arr[i];//记录取出的数据,arr[]下标大的放在前面,输出的时候注意从后往前输出
15     return true;
16 }
17 return false;
18 }//主函数
19 int main(){
20 int i;
21 while(~scanf("%d %d",&n,&k)){
22         j=0;//初始化
23 for(i=0;i<n;i++){
24   scanf("%d",&arr[i]);
25 }
26
27 if(dfs(0,0)){
28     printf("YES\n");
29     j=j-1;
30     while(j>=0){//输出
31         printf("%d ",result[j--]);
32         }
33          printf("\n");
34     }
35 else
36      printf("NO\n");
37 }
38 return 0;
39 }

**拓展:那怎样写,才能将所有的结果都输出呢?大部分题目还是要求这样的。(dfs+多组记录+还原)

思路:与前面的大同小异,异在:输入数据的时候,从下标1开始存,参数sum也定义为全局变量;及时还原,并且找到了就输出,不在主函数里来输出。(if、else结构)

输入:4 12

4 8 1 7

输出:YES

4 1 7

4 8

代码<c语言>:

#include<stdio.h>
int n,k,arr[21],result[21],sum=0,flag=0;
void dfs(int i)//(if、else部分)
{
    int j;
    if(i==n+1) //已经全部做好选择(每一层都选择好了)
    {
        if(sum==k) //正好与所要求的相等
        {
            ++flag;
            if(flag==1)//改组数据第一次找到符合的,输出YES,再次找到就不用输YES啦
                printf("YES\n");
            for(j=1; j<=n; j++)//找到就输出
            {
                if(result[j])
                    printf("%d ",arr[j]);
            }
            printf("\n");
        }
        else
            return;
    }
    else
    {
        //对下一个数进行选择        //1.取数        dfs(i+1);//2.不取数
        sum+=arr[i];
        result[i]=1;//表示用过
        dfs(i+1);//对下一个数进行选择
        result[i]=0;//表示没用过,还原
        sum=sum-arr[i];
    }
    return;
}
int main()
{
    int i;
//输入
    while(~scanf("%d %d",&n,&k))
    {
        flag=0;//初始化
        sum=0;
        for(i=1; i<=n; i++) //从数组下标1开始存
        {
            scanf("%d",&arr[i]);
        }
        dfs(1);//第一层,第一个数,所以要n层
        if(flag==0)
            printf("NO\n");
    }
    return 0;
}

3.组合数——南阳32(dfs+记录+还原)

题目描述:http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=32

题目要求:找出从自然数1、2、... 、n(0<n<10)中任取r(0<r<=n)个数的所有组合。还要求输出时,每一个组合中的值从大到小排列,组合之间按逆字典序排列。

分析:先不看输出要求,要确定r个数,则有r层,递归r次;那每一层有哪些选择呢?以第count(1<count<r)层为例,也就是要确定第count个数,这时第count-1个数已经确定了,第count个数一定在它之后,这样取出来的数就有一定的顺序,那第count个数能取1~n的最后一个数吗?自然是不可以的,不然剩下的r-count个数放哪呢?所以第count个数的下标要小于n-(r-count),所以每一层的选择是:“前一个数的下标”<i<n-r+count。每一层的数的下标可以用result[]保存。

考虑输出要求,逆字典序,所以输入时,倒着存,以n,n-1,n-2.......2,1存入数组,并且以下标1开始(方便)。

代码<c语言>:

#include <stdio.h>
int arr[10],result[10],len,num;//result存取出来的数的下标,len——数组的长度,num——要取出数是个数
void dfs(int count){//(if、else结构)
    int i;
    if(count==num+1) //num个数都取出来了
    {
        for(int j=1; j<=num; j++)
        {
        printf("%d",arr[result[j]]);
        }
        printf("\n");
    }
    else{
        for(i=result[count-1]+1; i<len-num+count; i++) //可选择的范围,其下标一定会在所取前一个数下标之后,在len-num+count之前(包括)它
        {
        result[count]=i;
        dfs(count+1);
        }
    }
        return;
}
int main()
{
    int n,r,i;
    scanf("%d %d",&n,&r);
    len=n+1;
    num=r;
    for(i=1; i<=n; i++) //数组长度为n+1,注意
    {
        arr[i]=n-i+1;//倒着存
    }
    dfs(1);
    return 0;
}

4.四色问题——code<vs>1116(dfs+还原)

题目描述:http://www.codevs.cn/problem/1116/

题目要求:给地图上的点用4种颜色涂色,求共有多少种涂法,要求相邻点的颜色不同。

分析:n个点,n层,n次递归;每一个点颜色有4种选择,但是要去掉相邻点的颜色。

代码<c语言>:

#include<stdio.h>
int a[10][10],b[10],c=0,n;//a[10][10]储存相邻关系,b[10]保存涂的颜色
void dfs(int x) //(if、else结构)x可以表示第几层,也可以表示第几个点
{
    int i,j;//if
    if(x==n+1) //表示所有点都涂完了
    {
        c++;
        return;//这里return了,下面就不用写else。
    }//else
    for(i=1; i<=4; i++) //涂四种颜色,四种选择 1,2,3,4表示四种颜色
    {
        //  b[x]=i;//涂色,放在这里不合适,抹去颜色只有两种途径,1.颜色可以这样涂,便可以dfs下去,等待回溯后,进行b[x]=0操作。2.颜色选错了,break后等待下一次选色,覆盖它的颜色。但问题在于若当前已经是1,2,3,4中最后一种颜色了,就覆盖不了了
        //应该先判断能不能涂,再进行操作
        for(j=1; j<=n; j++)
        {
            if(a[x][j]&&b[j]==i)//找出与当前 点 相邻,并且颜色相同的,就说明当前颜色涂错了
                break;
        }
        if(j==n+1) //正常跳出,不是break出来的,就说明可以涂这种颜色
        {
            b[x]=i;//涂色
            dfs(x+1);
            b[x]=0;//涂完过后,还原
        }
    }
    return;
}
int main()
{
//输入
    int i,j;
//freopen("1.txt","r",stdin);
    scanf("%d",&n);
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            scanf("%d",&a[i][j]);
        }
    }
    dfs(1);
    printf("%d",c);
    return 0;
}

5.素数环——南阳488(dfs+记录+还原)

题目描述:http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=488

题目要求:若形成素数环,从1开始(其实第一个位置上的数已经确定),从大到小输出

分析:n个数,n层,可以代表素数环的n个位置;每个位置,有1~n个选择,只是,前面位置用过的,不能再用,这个数与前一位置上的数之和为不为素数,不能用。

代码<c语言>:

//超时问题 1.将相邻位之和是否为素数 的判断放在 取数的阶段,而不是先将数放好,再判断素数问题2.若n为奇数,一定不成素数环 3.判断素数,用埃氏筛选。
//特殊情况? n=1,1为奇数,但是可以成素数环
#include<stdio.h>
#include<math.h>
int n,a[21],result[21],A[50],flag2;//result数组从下标1开始存数
//埃氏筛选出素数,i为素数,A[i]=0;i不为素数,A[i]=1
void AS()
{
    int q,i;
    q=sqrt(50);
    A[0]=1;
    A[1]=1;//A[i]=1表示i不为素数
    for(i=0; i<=q; i++)
    {
        if(!A[i])//如果i为素数,则i的倍数不为素数
            for(int j=i+i; j<50; j=i+j) //j为i的倍数
            {
                A[j]=1;
            }
    }
}
void dfs(int count)
{
    int i,j,flag=1;//flag用来标记相邻位是否是素数,flag2用来标记是否形成过素数环
    if(count==n+1) //已经形成一个环
    {
        //判断是否是素数环
        /*  for(j=1;j<n;j++){
        if(A[result[j]+result[j+1]])//相邻位置之和不是素数。将素数的判断放在 每个位置上的数都选好之后,花时间长
        flag=0;
        }*/
        if(A[result[n]+result[1]])//头尾
            flag=0;
        if(flag) //是素数环
        {
            flag2=1;//表示形成过素数环
            for(j=1; j<=n; j++)
            {
                printf("%d ",result[j]);
            }
            printf("\n");
        }
    }
    else
    {
        for(i=1; i<=n; i++) //每一个位置(每一层)有n种选择
        {
            if(a[i]&&!A[i+result[count-1]]) //之前没有用过 并且 相邻位置数之和为素数 这样的数才符合要求(将素数判断放在 选择数之前,节约时间)
            {
                result[count]=i;
                a[i]=0;//i用过后就用0标记
                dfs(count+1);
                a[i]=i;//还原
            }
        }
    }
    return;
}
int main()
{
    int k=1;
    //输入
    AS();
    while(~scanf("%d",&n)&&n)
    {
        flag2=0;//初始化
        printf("Case %d:\n",k++);
        if(n==1)
            printf("1\n");
        else if(n%2)
            printf("No Answer\n");
        else
        {
            for(int i=1; i<=n; i++)
            {
                a[i]=i;//将数放入数组
            }
            result[1]=1;//已经确定
            a[1]=0;//用过了
            dfs(2);//确定第一个数
            if(!flag2)//说明一组都没找着
                printf("No Answer\n");
        }
    }
    return 0;
}

6.Lake Counting——poj488(dfs)

题目描述:http://poj.org/problem?id=2386

要求:输出水洼的个数

分析:其实就是简单的深度搜索(8种选择),搜索不下去的时候,一个水洼就确定了,又开始下一个搜索。‘W‘代表水,只有‘W’能走,走过的地方变为‘.’,避免重复走过,不必还原。

代码<c语言>:

#include<stdio.h>
int m,n;
char a[105][105];
void dfs(int x,int y)
{
    int nx,ny,dx,dy;
    a[x][y]=‘.‘;//将遍历过的点,由‘W’变为‘.‘,避免再次遍历
    for(dx=-1; dx<=1; dx++)//八个方向,8种选择
    {
        for(dy=-1; dy<=1; dy++)
        {
            nx=x+dx;//八个方向遍历,不能写成nx,ny
            ny=y+dy;
            if(a[nx][ny]==‘W‘&&nx>=0&&nx<m&&ny>=0&&ny<n) //若下一个方向上的点 是水洼,且没超过边界,继续遍历
            {
                dfs(nx,ny);
            }
        }
    }
    return;
}
int main()
{
    int i,j,c=0;
//输入
//freopen("1.txt","r",stdin);
    scanf("%d %d",&m,&n);
    getchar();//注意
    for(i=0; i<m; i++)
    {
        for(j=0; j<n; j++)
        {
            scanf("%c",&a[i][j]);
        }
        getchar();//注意
    }
    for(i=0; i<m; i++)
    {
        for(j=0; j<n; j++)
        {
            if(a[i][j]==‘W‘) //只遍历‘W‘
            {
                dfs(i,j);//遍历结束一次,构成一个水洼
                c++;
            }
        }
    }
    printf("%d",c);
    return 0;
}

原文地址:https://www.cnblogs.com/li-yaoyao/p/9306824.html

时间: 2024-10-08 13:07:09

深度搜索入门的相关文章

机器学习和深度学习入门总结

本菜鸟入门机器学习也有一段时间了,有那么一丢丢的感悟,在这里做一点总结.介绍一下机器学习理论和实践的学习心得. 相关教材 数学基础 高数.线性代数这就没啥好说的,就是大学工科的必修科目. 统计机器学习 李航的蓝皮书和周志华的西瓜书可以说是国内的比较经典的教材,这两位也是国内人工智能领域的领军人物. 深度学习 强烈推荐花书,这可以说是深度学习方面的权威教材.除此以外还有吴恩达的讲义和教学视频,网上都可以找到. 小白入门教材 前面推荐的书籍,虽然算得上入门教材,但可能对于小白来说,不是很容易接受.这

[LeetCode] Sum Root to Leaf Numbers dfs,深度搜索

Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number. An example is the root-to-leaf path 1->2->3 which represents the number 123. Find the total sum of all root-to-leaf numbers. For example, 1 / 2 3 T

DFS深度搜索的一般思想

对于无向图来说DFS深度搜索 递归思想 //深度优先搜索DFS的一般实现 void DFS(MGraph G,int i)//DFS递归思想 { int j; visited[i]=TRUE;//设置Node已经被访问 printf("%c",G.vexs[i]); for(j=0;j<numVertexes;j++)//对此Node跟Node2(j)遍历 如果arc[][]==1则表明当前DFS的Node与Node2(j)连通,且满足Node2未被访问的条件下 则对Node2进

搜索入门之dfs--经典的迷宫问题解析

今天来谈一下dfs的入门,以前看到的dfs入门,那真的是入门吗,都是把dfs的实现步骤往那一贴,看完是知道dfs的步骤了,但是对于代码实现还是没有概念.今天准备写点自己的心得,真的是字面意思--入门. DFS,即深度优先搜索,是一种每次搜索都向尽可能深的地方去搜索,到达尽头时再回溯进行其他结点去搜索的搜索策略.形象的说,这是一种“不撞南墙不回头”的策略. 其实也就是遍历,只不过不像一个线性数组的遍历那么直观罢了.说到回溯,每次看到这个词我都得思考一会,到底是怎么用栈进行回溯的呢?今天实际操作了一

创建二叉树 树的深度搜索 广度搜索

树的深度搜索 与树的前序遍历同理 根节点->左孩子->右孩子  树的广度搜索 与树的层次遍历同理 一层一层遍历内容 深度搜索 采用stack的适配器 先进后出原则  而广度搜索采用的queue适配器 先进先出原则 二者正好满足 搜索需求 简要代码如下: #include <iostream> #include <stack> #include <queue> #include <malloc.h> using namespace std; typ

[LeetCode] Convert Sorted List to Binary Search Tree DFS,深度搜索

Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST. Hide Tags Depth-first Search Linked List 这题是将链表变成二叉树,比较麻烦的遍历过程,因为链表的限制,所以深度搜索的顺序恰巧是链表的顺序,通过设置好递归函数的参数,可以在深度搜索时候便可以遍历了. TreeNode * help_f(Lis

[LeetCode] Maximum Depth of Binary Tree dfs,深度搜索

Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. Hide Tags Tree Depth-first Search 简单的深度搜索 #include <iostream> using namespace std; /** *

[LeetCode] Path Sum II 深度搜索

Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum. For example:Given the below binary tree and sum = 22, 5 / 4 8 / / 11 13 4 / \ / 7 2 5 1 return [ [5,4,11,2], [5,8,4,5] ] Hide Tags Tree Depth-first

深度搜索应用之黑白图像(非递归)

深度搜索应用之黑白图像(非递归) 前言: 使用深度搜索,有两个方法:递归,栈.本质是栈. 递归有一个缺陷,栈溢出.栈有一个缺陷,程序相对递归更复杂. 练习题: 输入一个n*n的黑白图像(1表示黑色,0表示白色),任务是统计其中八连块的个数.如果两个黑格子有公共边或者公共顶点,就说它们属于同一个八连块.(题意是让求连在一起的块有几个,图见书本)   使用递归求解:点这里 使用栈求解: 1 #include <iostream> 2 #include <memory.h> 3 #inc