Depth First Search(DFS) ---- 深度优先搜寻 (学习笔记)

Depth First Search(DFS) ---- 深度优先搜寻 (学习笔记)

  • 首先,什么是DFS呢?

DFS是一种用于遍历或搜索树或图的算法。
过程如下
(1)从图(树)中某个顶点v出发,访问v;
(2)找出刚访问过的顶点的第一个未被访问的邻接点,访问该结点。以该结点为新结点,重复此步骤,直至刚访问过的顶点没有未被访问的邻接点为止;
(3)返回前一个访问过且仍有未被访问的邻接点的顶点,找出该顶点的下一个未被访问的邻接点,访问该顶点;
(4)重复(2)和(3),直至图(树)中所有顶点都被访问过,搜索结束。

DFS的核心思想就是回溯法

回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

补充知识:图的存储结构:邻接矩阵、邻接表
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
DFS模板如下(来自一位很厉害的学姐嘿嘿嘿):
在这里插入图片描述

例题:

1. 51Nod-2060 全排列

输入一个整数n(n <= 9),输出1、2、3、······、n这n个数的全排列(按照字典序输出)。
输入

一个整数n

输出

多行,每行表示一种排列,行内使用空格分隔相邻两数。

输入样例

3

输出样例

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

我的代码:

#include <iostream>

using namespace std;

int p_array[10]; // 位置数组 表示状态
int ans[10]; // 储存排列结果

void A( int n , int pos )
{
    if ( pos >= n+1 )
    {   // 越界 退出
        for ( int i=1 ; i<pos-1; i++ )
            cout<<ans[i]<<" ";
        cout<<ans[pos-1]<<endl;
        return;
    }
    int i;
    for ( i=1 ; i<=n ; i++ )
    {   // 在p_array里面找还没有用过的数
        if ( p_array[i] == 0 )
        {   // 若还没有用
            p_array[i]=1;
            ans[pos]=i;
            A( n , pos+1 ); // 继续执行下一层函数
            p_array[i]=0;   // 函数执行完毕,当前数字标记为“未使用”,继续向后查找数字
        }
    } // 查找完所有数字,回溯进度
    return;
}

int main()
{
    int n; cin>>n;
    for ( int i=1 ; i<10 ; i++ )
        p_array[i]=0; //初始化
    A( n , 1 );
    return 0;
}

2. HDU-1016----Prime Ring Problem

Problem Description
A ring is compose of n circles as shown in diagram. Put natural number 1, 2, …, n into each circle separately, and the sum of numbers in two adjacent circles should be a prime.

Note: the number of first circle should always be 1.

Input
n (0 < n < 20).

Output
The output format is shown as sample below. Each row represents a series of circle numbers in the ring beginning from 1 clockwisely and anticlockwisely. The order of numbers must satisfy the above requirements. Print solutions in lexicographical order.

You are to write a program that completes above process.

Print a blank line after each case.

Sample Input

6
8

Sample Output

Case 1:
1 4 3 2 5 6
1 6 5 2 3 4

Case 2:
1 2 3 8 5 6 7 4
1 2 5 8 3 4 7 6
1 4 7 6 5 8 3 2
1 6 7 4 3 8 5 2

这题折腾了我一下午,我一直以为是算法出了问题,结果是素数的判断出问题了(捂脸)
废话不多说,上代码:

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>  // 包含memset()
using namespace std;
int pos_array[25];
int ans[25];
int n;

int Prime( int x )
{   // 以后判断素数用这个,不要再简单问题上在浪费时间了......浪费了一下午时间...
    for ( int i=2 ; i*i<=x ; i++ )
    {
        if ( x%i ==0 )
            return 0;  // 不是素数
    }
    return 1; // 是素数
}

void dfs( int pos ) // depth first search
{
    if ( pos == n+1 && Prime( ans[pos-1]+1 ) )
    {   // pos越界一个单位,判断末位数字与1的和是否为素数
        for ( int i=1 ; i<=n-1 ; i++ ) // 输出
            printf("%d ", ans[i]);
        printf("%d\n", ans[n]);
        return;
    }

    int i;
    for ( i=2 ; i<=n ; i++ )
    {
        if ( pos_array[i]==0 && Prime( i+ans[pos-1]) )
        {   // 判断这个数字有没有被使用&&与前一个数的和是否为素数
            pos_array[i]=1;
            ans[pos]=i;
            dfs(pos+1);
            pos_array[i]=0;
        }
    }
    return;
}
int main()
{
    int num=0;  // num记录实例的个数
    while ( scanf("%d", &n) != EOF )
    {
 /*      for ( int i=1 ; i<=20 ; i++ )
            pos_array[i]=0; // 初始化数字状态 */
        num++;
        printf("Case %d:\n",num);
        memset( pos_array , 0 , sizeof(pos_array) );  // memset用法讲解见https://www.runoob.com/cprogramming/c-function-memset.html
        ans[1]=1; // 第一个数一定是1
        dfs( 2 );
        printf("\n");
 /*       // 顺时针输出
       for ( int r=1 ; r<=row ; r++ )
       {
            for ( int c=1 ; c<n ; c++ )
                printf("%d ", ans[r][c]);
            printf("%d\n", ans[r][n]);
       }
        // 逆时针输出
        for ( int r=1 ; r<=row ; r++ )
        {
            printf("1 ");
            for ( int c=n ; c>2 ; c-- )
                printf("%d ", ans[r][c]);
            printf("%d\n", ans[r][2]);
        }
       printf("\n");           不需要这样分开输出,因为dfs中可以直接找到逆时针的排列,
                                     只不过是反了一下,把末尾的数字排到了前面*/

    }
    return 0;
}

3. OpenJ_Bailian 4123----马走日

总时间限制:
1000ms
内存限制:
1024kB

描述

马在中国象棋以日字形规则移动。

请编写一段程序,给定n*m大小的棋盘,以及马的初始位置(x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点。

输入
第一行为整数T(T < 10),表示测试数据组数。
每一组测试数据包含一行,为四个整数,分别为棋盘的大小以及初始位置坐标n,m,x,y。(0<=x<=n-1,0<=y<=m-1, m < 10, n < 10)
输出
每组测试数据包含一行,为一个整数,表示马能遍历棋盘的途径总数,0为无法遍历一次。
样例输入

1
5 4 0 0

样例输出

32

这道题忘记了标记起点,也是找了好长时间才发现
代码如下:

#include <iostream>
#include <string.h>
using namespace std;
int n, m, ans;
int table[10][10];
void dfs( int x , int y , int step )
{
    /* 注:该数组必须放在函数内,不能作为全局变量,否则无法读取 */
    int next[9][2]={ {0,0},
                        {2,1},  //下一步如何走
                        {1,2},
                        {-1,2},
                        {-2,1},
                        {-2,-1},
                        {-1,-2},
                        {1,-2},
                        {2,-1}  };
    if ( step==n*m )
    {   // 到达最后一个点
        ans++;
        return;
    }
    int i, tx, ty;
    // 枚举8种走法
    for ( i=1 ; i<=8 ; i++ )
    {
        tx=x+next[i][0];    // 横坐标变换
        ty=y+next[i][1];    // 纵坐标变换
        if ( tx<0 || tx>n-1 || ty<0 || ty>m-1 )
        {   // 判断是否越界
            continue;
        }
        if ( table[tx][ty]==0 )
        {   // 若当前点还没有走过
            table[tx][ty]=1;
            dfs( tx , ty , step+1 ); // 步数不要忘记加1
            table[tx][ty]=0;
        }
    }
    return;
}
int main()
{
    int T; cin>>T;
    while ( T-- )
    {
        //memset( table , 0 , sizeof(table));
        for ( int r=0 ; r<10 ; r++ )
            for ( int c=0 ; c<10 ; c++ )
                table[r][c]=0;
        int x, y;
        cin>>n>>m>>x>>y;
        ans=0;
        // 忘了标记起点了哈哈哈
        table[x][y]=1;
        dfs( x , y , 1 );
        cout<<ans<<endl;
    }
    return 0;
}

拓展练习: POJ 1724----Roads

之后有时间可以做这题练一练手,有点难度

ROADS
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 22351 Accepted: 7788

Description
N cities named with numbers 1 … N are connected with one-way roads. Each road has two parameters associated with it : the road length and the toll that needs to be paid for the road (expressed in the number of coins).
Bob and Alice used to live in the city 1. After noticing that Alice was cheating in the card game they liked to play, Bob broke up with her and decided to move away - to the city N. He wants to get there as quickly as possible, but he is short on cash.

We want to help Bob to find the shortest path from the city 1 to the city N that he can afford with the amount of money he has.

Input
The first line of the input contains the integer K, 0 <= K <= 10000, maximum number of coins that Bob can spend on his way.
The second line contains the integer N, 2 <= N <= 100, the total number of cities.

The third line contains the integer R, 1 <= R <= 10000, the total number of roads.

Each of the following R lines describes one road by specifying integers S, D, L and T separated by single blank characters :

S is the source city, 1 <= S <= N
D is the destination city, 1 <= D <= N
L is the road length, 1 <= L <= 100
T is the toll (expressed in the number of coins), 0 <= T <=100

Notice that different roads may have the same source and destination cities.

Output
The first and the only line of the output should contain the total length of the shortest path from the city 1 to the city N whose total toll is less than or equal K coins.
If such path does not exist, only number -1 should be written to the output.

Sample Input

5
6
7
1 2 2 3
2 4 3 3
3 4 2 4
1 3 4 1
4 6 2 1
3 5 2 0
5 4 3 2

Sample Output

11

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值