蓝桥杯 Day11 java组 DFS

2023-11-15

        所谓暴力法,是把所有可能的情况都罗列出来,然后逐一检查,从中找到答案。

        暴力法往往是“低效”的代名词。不过相对其他“高效”算法,暴力法不仅简单且编码量一般都更短更好写。所以当拿到一个题目后,首先想想如何用暴力法解决。如果暴力法的代码能满足题目要求的时间和空间限制,就用暴力法。若暴力法不能满足要求,也可以把它当成对照,用来验证高级算法的正确性。

        暴力搜索的思路很简单,但是有的时候操作起来并不容易。比如蓝桥杯中常见的迷宫问题: 

  • 如何用数据结构表示一个迷宫?
  • 如何找到一条从起点到终点的路径?
  • 如何列举从起点到终点的所有可能的路径?

        深度优先搜索(DFS, Depth-First Search)和宽度优先搜索(BFS, Breadth-First Search,或称为广度优先搜索)是基本的暴力技术,常用于解决图、树的遍历问题。 

        以老鼠走迷宫为例说明 BFS 和 DFS 的原理。迷宫内的路错综复杂,老鼠从入口进去后,怎么才能找到出口。有两种方案: 

  • 一只老鼠走迷宫。它在每个路口,都选择先走右边(当然,选择先走左边也可以),能走多远就走多远;直到碰壁无法再继续往前走,然后往回退一步,这一次走左边,然后继续往下走。用这个办法,只要没遇到出口,就会走遍所有的路,而且不会重复(这里规定回退不算重复走)。这个思路就是 DFS。
  • 一群老鼠走迷宫。假设老鼠无限多,这群老鼠进去后,在每个路口,都派出部分老鼠探索所有没走过的路。走某条路的老鼠,如果碰壁无法前行,就停下;如果到达的路口已经有别的老鼠探索过了,也停下。很显然,在遇到出口前,所有的道路都会走到,而且不会重复。这个思路就是 BFS。

        具体编程时,一般用队列这种数据结构来实现 BFS ,即 “BFS = 队列”;

        而 DFS 一般用递归实现,即 “DFS = 递归”。 

递归

排列(自写全排列算法)

设数字是 {1 2 3 4 5......n},那么递归求全排列

  1. 让第一个数不同,得到 n 个数列。其办法是:把第 1 个和后面每个数交换。

    • 1 2 3 4 5......n
    • 2 1 3 4 5......n

    以上 n 个数列,只要第一个数不同,不管后面 n−1 个数是怎么排列的,这 n​ 个数列都不同。 这是递归的第一层。

  2. 继续:在上面的每个数列中,去掉第一个数,对后面的 n−1​ 个数进行类似的排列。例如从上面第 2 行的{2 1 3 4 5......n}进入第二层(去掉首位 2):

    • 1 3 4 5......n
    • 3 1 4 5......n
    • ......
    • n 3 4 5......1

    以上 n-1 个数列,只要第一个数不同,不管后面 n-2 个数是怎么排列的,这 n-1 个数列都不同。

    这是递归的第二层。

  3. 重复以上步骤,直到用完所有数字

import java.util.Scanner;

public class Main {
    public static int[] a = new int[3];

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        for (int i = 0; i <= 2; i++) {
            a[i] = scanner.nextInt();
        }
        dfs(0, 2);
    }

    public static void dfs(int s, int t) {//从第s到第t的全排列
        if (s == t) {//可以在这里控制数列长度(如果需要打印 nn 个数中任意 mm 个数的排列,例如在 44 个数中取任意 33 个数的排列)
            for (int i = 0; i <= 2; i++) {
                System.out.print(a[i] + " ");
            }
            System.out.println();
            return;
        }
        for (int i = s; i <= t; i++) {
            swap(s, i);
            dfs(s + 1, t);
            swap(i, s);
        }
    }

    public static void swap(int i, int j) {
        int temp;
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

以上不能按从小到大的顺序打印排列

第一题 迷宫

 C++ 纯暴力

#include<bits/stdc++.h>
using namespace std;
const int n=10;
char mp[n+2][n+2];       //用矩阵mp[][]存迷宫图。把静态数组定义在全局
bool vis[n+2][n+2];      //判断点是否曾走过,是“记忆化”功能
int ans = 0;
int cnt = 0;
bool dfs(int i, int j){
    if (i<0 || i>n-1 || j<0 || j>n-1)   return true; //走出了迷宫,停止
    if (vis[i][j])  return false;   //如果已经搜过,说明兜圈子了,走不出去
    cnt++;                          //统计dfs()了多少次
    vis[i][j] = true;               //标记已搜索
    if (mp[i][j] == 'L')  return dfs(i, j - 1);   //往左,继续dfs
    if (mp[i][j] == 'R')  return dfs(i, j + 1);   //往右
    if (mp[i][j] == 'U')  return dfs(i - 1, j);   //往上
    if (mp[i][j] == 'D')  return dfs(i + 1, j);   //往下
}
int main(){
    //本题是填空题,直接输出答案:
    // cout << 31;    return 0;
    //如果不是填空,就写下面的代码:
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            cin >> mp[i][j];             //读取迷宫图
    for (int i = 0; i < n; i++)          //对每个点,判断是否能走出去
        for (int j = 0; j < n; j++){
            memset(vis, 0, sizeof(vis)); //搜索每个点前,都清空vis[]
            if(dfs(i, j))   ans++;       //点mp[i][j]能走出去,统计答案
        }
    cout <<"ans="<< ans <<", cnt="<<cnt<< endl;       //输出答案
    return 0;
}
  1. 走出了迷宫,返回 true,代码是第 99 行。
  2. 走不出迷宫,返回 false,代码是第 1010 行。什么情况下走不出迷宫?必然是兜圈子,回到了曾经走过的点。用 vis[i][j]vis[i][j] 记录点 (i, j)(i,j) 是否曾经走过,如果走过,就是兜圈子了。

在做每个竞赛题时,都应该分析复杂度,看代码是否能在限定的时间和空间内完成。如果迷宫有 n 行 n 列,做一次 dfs(),那么最多需要走遍所有的点,即 O(n^2) 次。代码的 25−29 行对 n^2​ 个点,每个点都做了一次 dfs(),所以总复杂度是 O(n^4) 的。 

#include<bits/stdc++.h>
using namespace std;
const int n=10;
char mp[n+2][n+2];
bool vis[n+2][n+2];
int solve[n+2][n+2];   //solve[i][j]=1表示这个点能走出去;=2表示出不去
int ans = 0;
int cnt = 0;
bool dfs(int i, int j){
    if (i<0 || i>n-1 || j<0 || j>n-1) return true;
    if(solve[i][j]==1)   return true;   //点(i,j)已经算过了,能出去
    if(solve[i][j]==2)   return false;  //点(i,j)已经算过了,出不去
    if (vis[i][j])       return false;
    cnt++;                   //统计dfs()了多少次
    vis[i][j] = true;
    if (mp[i][j] == 'L'){
        if(dfs(i, j - 1)){ solve[i][j] = 1;return true;}  
                          //回退,记录整条路径都能走出去
        else             { solve[i][j] = 2;return false;} 
                          //回退,记录整条路径都出不去
    }
    if (mp[i][j] == 'R') {
        if(dfs(i, j + 1)){ solve[i][j] = 1;return true;}
        else             { solve[i][j] = 2;return false;}
    }
    if (mp[i][j] == 'U') {
        if(dfs(i - 1, j)){ solve[i][j] = 1;return true;}
        else             { solve[i][j] = 2;return false;}
    }
    if (mp[i][j] == 'D') {
        if(dfs(i + 1, j)){ solve[i][j] = 1;return true;}
        else             { solve[i][j] = 2;return false;}
    }
}
int main(){
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            cin >> mp[i][j];
    memset(solve, 0, sizeof(solve));
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++){
            memset(vis, 0, sizeof(vis));
            if(dfs(i, j))   ans++;
        }
    cout <<"ans="<< ans <<",cnt="<<cnt<< endl;
    return 0;
}

上面是优化后的代码,代码中我用 solve[][] 完成了标记整个路径>这一工作:

  • 当 solve[i][j]=1 时,表示点 (i, j)(i,j) 能走出去;
  • 当solve[i][j]=2 时,表示出不去。

 第二题 方格分割

public class Main {
    private static boolean[][] visited = new boolean[7][7];
    private static int[][] direction = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    private static int count = 0;

    public static void main(String[] args) {
        for (int i = 0; i < 7; i++) {
            for (int j = 0; j < 7; j++) {
                visited[i][j] = false;
            }
        }
        visited[3][3] = true;
        dfs(3, 3);
        System.out.println(count / 4);
    }

    public static void dfs(int x, int y) {
        if (x == 0 || x == 6 || y == 0 || y == 6) {

            count++;
            return;
        }
        visited[x][y] = true;
        visited[6 - x][6 - y] = true;
        for (int i = 0; i < 4; i++) {
            int x1 = x + direction[i][0];
            int y1 = y + direction[i][1];
            if (x1 < 0 || x1 > 6 || y1 < 0 || y1 > 6) {
                continue;
            }
            if (visited[x1][y1]!=true) {
                dfs(x1, y1);
            }
        }
        visited[x][y] = false;
        visited[6 - x][6 - y] = false;
    }
}

第三题 正则问题

样例输入

((xx|xxx)x|(x|xx))xx

样例输出

6

import java.util.Scanner;

public class Main {
    private static String string;
    private static int len;
    private static int point = 0;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        string = scanner.next();
        len = string.length();
        System.out.println(dfs(string));
    }

    private static int dfs(String s) {
        int tempLen = 0;
        int ans = 0;

        while (point < len) {
            if (s.charAt(point) == '(') {
                point++;
                tempLen = tempLen + dfs(string);
            } else if (s.charAt(point) == ')') {
                point++;
                return Math.max(ans,tempLen);
            } else if (s.charAt(point) == 'x') {
                point++;
                tempLen++;
            } else {
                point++;
                ans = tempLen;
                tempLen = 0;
            }
        }
        return Math.max(ans,tempLen);
    }
}

 第四题 寒假作业

题目描述

现在小学的数学题目也不是那么好玩的。 看看这个寒假作业:

   □ + □ = □
   □ - □ = □
   □ × □ = □
   □ ÷ □ = □

每个方块代表 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

就算两种解法。(加法,乘法交换律后算不同的方案)

你一共找到了多少种方案?

public class Main {
    private static int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
    private static int ans = 0;
    public static void main(String[] args) {
        dfs(0, 12);
        System.out.println(ans);
    }

    public static void dfs(int s, int t) {//剪枝
        if (s == 3) {
            if (!(a[0] + a[1] == a[2])) {
                return;
            }
        }
        if (s == 6) {
            if (!(a[3] - a[4] == a[5])) {
                return;
            }
        }
        if (s == 9) {
            if (!(a[6] * a[7] == a[8])) {
                return;
            }
        }
        if (s == 12) {
            if(a[9]*a[10]==a[11]){
                ans++;
            }
        }
        for (int i = s; i <= t; i++) {
            swap(i, s);
            dfs(s + 1, t);
            swap(s, i);
        }

    }

    public static void swap(int i, int j) {
        int temp;
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

64种

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

蓝桥杯 Day11 java组 DFS 的相关文章

随机推荐

  • openresty+lua安装

    一 下载软件 下载ngx openresty xxx tar gz并解压 wget https openresty org download ngx openresty 1 9 7 1 tar gz ngx openresty xxx bu
  • 将包含children的数据解析构成iview的cascader或者树行需要的结构

    function convertTree rst const result 遍历 tree rst forEach item gt 解构赋值 let value value label label children children ite
  • 使用VS Code开发Arduino

    文章目录 目的 软件安装 快速使用 更多说明 编译输出时中文乱码 Arduino扩展配置说明 使用 arduino cli 总结 目的 Arduino官方的IDE作为编辑器的功能挺简陋的 用起来并不是很舒服 相比较之下用VS Code Vi
  • 面试官:说说TCP如何实现可靠传输

    今天来讲一下TCP是如何保证可靠传输的 这也是面试常问的一个题目 这个问题不单止能看出你是否真的了解TCP原理 更看出你是否有一个总结的能力 我们从三个部分来讲TCP是如何实现可靠传输的 滑动窗口 首先是讲TCP中的滑动窗口 它和TCP的可
  • 论文阅读: GeoNet: Unsupervised Learning of Dense Depth, Optical Flow and Camera Pose(CVPR2018)

    CVPR2018 GeoNet Unsupervised Learning of Dense Depth Optical Flow and Camera Pose 提出了一个联合估计深度 光流和pose的网络 这是在left right c
  • Javascript设计模式-04-工厂模式

    Javascript设计模式 04 工厂模式 简单工厂 抽象工厂 简介 工厂模式定义一个用于创建对象的接口 这个接口由子类决定实例化哪一个类 该模式使一个类的实例化延迟到了子类 而子类可以重写接口方法以便创建的时候指定自己的对象类型 个人理
  • webview跳转第三方APP

    hello 又是我鑫鑫 前言 这吃给大家带来的博客是关于webview跳转第三方APP的 相信这个问题也为难过各位 那么话不多说 我直接上代码 MainActivity java 这里的活动名我没有改 使用的话 将所有的Contact Cu
  • C规范编辑笔记(十三)

    往期文章 C规范编辑笔记 一 C规范编辑笔记 二 C规范编辑笔记 三 C规范编辑笔记 四 C规范编辑笔记 五 C规范编辑笔记 六 C规范编辑笔记 七 C规范编辑笔记 八 C规范编辑笔记 九 C规则编辑笔记 十 C规范编辑笔记 十一 C规范编
  • 线性表技巧之Note001-链表的最后一个节点

    找到单链表的尾节点 通常我们遍历单链表的代码如下 list 指向单链表的头节点 因此 list gt next 指向链表的第一个节点 LNode node list gt next while node NULL node node gt
  • Qt项目环境构建

    工欲善其事必先利其器 使用Qt来进行开发 得先配置好一个合适的环境 下面是我关于Qt项目环境构建的一些小结 Qt的项目构建主要依赖 pro文件 和 pri文件 include包含文件 提供pro的复用性高的东西给多个项目包含 所以新建一个Q
  • mysql query 查询_mysql提供了explain query_sql进行查询分析

    mysql提供了explain query sql进行查询分析 下边是一些参数说明 ID Query Optimizer 所选定的执行计划中查询的序列号 Select type 所使用的查询类型 主要有以下这几种查询类型 DEPENDENT
  • 面试时这样介绍算法,想不高薪都难,排序算法(冒泡排序)

    算法背景 冒泡排序是一种简单的排序算法 它重复地遍历要排序的数列 一次比较两个元素 如果他们的顺序错误就把他们交换过来 遍历数列的工作是重复地进行直到没有再需要交换 也就是该数列已经排序完成 这个算法的名字由来是因为越大的元素会经由交换慢慢
  • OpenCV-Python自适应直方图均衡类CLAHE及方法详解

    一 引言 对比度受限的自适应直方图均衡在OpenCV中是通过类CLAHE来提供实现的 老猿没研究过C 中的应用 但OpenCV Python中应用时与普通的Python类构建对象的机制有所不同 老猿做了相关测试 在此简单介绍一下 二 CLA
  • 为什么 API 治理需要内部倡导

    API 治理旨在帮助人们通过 API 实现最大价值 但是 只有了解 API 是什么以及 API 的重要性 并且认识到 API 治理是在帮助他们而不是监管他们 才能实现这一目标 这就是为什么在任何 API 治理举措中都必须包括内部 API 倡
  • 修改mesh的clolors属性

    using UnityEngine using System Collections public class ExampleClass MonoBehaviour void Start Mesh mesh GetComponent
  • Android开发笔记 自定义AlertDialog

    近期有个需求需要在自定义AlertDialog上添加一个输入框 并拿到输入的信息发送给后台 开发中有遇到些之前没有接触过的问题 所以记录下来 如果需要自定义的话 AlertDialog mDialog new AlertDialog Bui
  • linux中安装gitlab,修改密码

    安装分为远程下载安装和本地安装 远程的总提示我阿里云版本不对 所以我使用的是本地安装 1 清华的gitlab安装包下载地址 https mirrors tuna tsinghua edu cn gitlab ce yum el7 C M O
  • python爬取汽车之家_python爬取 汽车之家(汽车授权经销商)

    一 爬虫的目标 打开汽车之家的链接 https www autohome com cn beijing 出现如下页面 我们的目标是 点击找车 然后出现如下图 我们要把图中的信息抓取到 二 实现过程 我们选择 宝马5系 然后点击找车 注意宝马
  • 华为二面被问Redis分布式锁,您是不是有点小瞧我了?

    之前写了两篇有关线程安全的文章 你管这叫线程安全 NET八股文 线程同步技术解读 分布式锁是 线程同步 的延续 最近首度应用 分布式锁 现在想想 分布式锁不是孤立的技能点 这其实就是跨主机的线程同步 进程内 跨进程 跨主机 Lock Mon
  • 蓝桥杯 Day11 java组 DFS

    所谓暴力法 是把所有可能的情况都罗列出来 然后逐一检查 从中找到答案 暴力法往往是 低效 的代名词 不过相对其他 高效 算法 暴力法不仅简单且编码量一般都更短更好写 所以当拿到一个题目后 首先想想如何用暴力法解决 如果暴力法的代码能满足题目