当前位置: 首页 > news >正文

蓝桥杯DFS算法题(java)

最大连通

链接:
https://www.lanqiao.cn/problems/2410/learning/

问题描述

小蓝有一个 30 行 60 列的数字矩阵,矩阵中的每个数都是 0 或 1 。


110010000011111110101001001001101010111011011011101001111110

010000000001010001101100000010010110001111100010101100011110 

001011101000100011111111111010000010010101010111001000010100 

101100001101011101101011011001000110111111010000000110110000 

010101100100010000111000100111100110001110111101010011001011 

010011011010011110111101111001001001010111110001101000100011 

101001011000110100001101011000000110110110100100110111101011 

101111000000101000111001100010110000100110001001000101011001 

001110111010001011110000001111100001010101001110011010101110 

001010101000110001011111001010111111100110000011011111101010 

011111100011001110100101001011110011000101011000100111001011 

011010001101011110011011111010111110010100101000110111010110 

001110000111100100101110001011101010001100010111110111011011 

111100001000001100010110101100111001001111100100110000001101 

001110010000000111011110000011000010101000111000000110101101 

100100011101011111001101001010011111110010111101000010000111 

110010100110101100001101111101010011000110101100000110001010 

110101101100001110000100010001001010100010110100100001000011 

100100000100001101010101001101000101101000000101111110001010 

101101011010101000111110110000110100000010011111111100110010 

101111000100000100011000010001011111001010010001010110001010 

001010001110101010000100010011101001010101101101010111100101 

001111110000101100010111111100000100101010000001011101100001 

101011110010000010010110000100001010011111100011011000110010 

011110010100011101100101111101000001011100001011010001110011 

000101000101000010010010110111000010101111001101100110011100 

100011100110011111000110011001111100001110110111001001000111 

111011000110001000110111011001011110010010010110101000011111 

011110011110110110011011001011010000100100101010110000010011 

010011110011100101010101111010001001001111101111101110011101

如果从一个标为 1 的位置可以通过上下左右走到另一个标为 1 的位置,则称两个位置连通。与某一个标为 1 的位置连通的所有位置(包括自己)组成一个连通分块。

请问矩阵中最大的连通分块有多大?

答案提交

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

思路

这题就是很明显的dfs。

这道题的题意就是,让我们找一个全为1的连通分块,因为会有很多种连通分块,我们只需要找到最长的,即含1最多的连通分块就可以,然后把1的个数输出即可。

就是我觉得这种需要你上下左右去遍厉二维数组的dfs题,一般都是以下思路(一般迷宫类的好像都是这么个思路):

        首先需要创建一个boolean[][]b数组,用来标记(i,j)这个点是否被访问过。也可以用int型的数组代替,0-未访问过,1-访问过。然后再创建存方向的二维数组f[][],一般用{{0,1},{0,-1},{1,0},{-1,0}}来表示下、上、右、左四个方向。然后还需要一个存题中数据的一个二维数组g[][]。然后还需要一个变量ans来存最终的结果。大体都是需要这些东西的,根据题意灵活变通即可。

然后下面来说一下这一题的思路:

        首先它dfs的思路是什么,就是我们先找一个切入点,这题比较特殊,这个切入点的值需要是'1',才有可能找到一个连通分块,如果切入点就是'0',那根本往下走不了了啊,刚开始就不满足条件了。所以在进行dfs之前,我们先遍厉一下二维数组g[][],如果g[i][j]=='1'&&b[i][j]==false,就说明点(i,j)就可以作为切入点。然后我们就可以从这个(i,j)点开始dfs。

        那么dfs该怎么写?

        通常来说dfs在这种矩阵题里面一般都是传两个参数,即x,y坐标,dfs(int x, int y)。然后dfs里大多数都会写这么一个判断条件,就是判断是否越界,如果越界了,我们直接return即可。如果没越界,我们就让b[x][y]为true,即我现在从(x,y)开始dfs,那么(x,y)这个点相当于我访问过了。然后我们就利用方向数组f[][],去在(x,y)这个点上去枚举上、下、左、右四种方向,然后每枚举一个方向,我们就用两个临时变量tx,ty去存储执行完该方向的坐标,同时还要再次判断更新后的坐标是否越界,是否被访问过,是否为'1',只有不越界、没被访问过、为''1,才能继续从这个点(tx,ty)进行进一步的dfs。

        由于我通常把ans设成static,所以dfs执行完之后,ans也就更新好了,在main里面直接输出即可。

最大连通-代码

import java.util.Scanner;
public class Main {
    static boolean [][] b = new boolean[30][60];  // 默认全为false
    static char [][]g = new char[30][60];
    static int [][]f = {{0,-1},{0,1},{-1,0},{1,0}}; // 上 下 左 右
    static int cnt = 0;
    public static void main(String[] args) {
        String []s = {
                "110010000011111110101001001001101010111011011011101001111110",
                "010000000001010001101100000010010110001111100010101100011110",
                "001011101000100011111111111010000010010101010111001000010100",
                "101100001101011101101011011001000110111111010000000110110000",
                "010101100100010000111000100111100110001110111101010011001011",
                "010011011010011110111101111001001001010111110001101000100011",
                "101001011000110100001101011000000110110110100100110111101011",
                "101111000000101000111001100010110000100110001001000101011001",
                "001110111010001011110000001111100001010101001110011010101110",
                "001010101000110001011111001010111111100110000011011111101010",
                "011111100011001110100101001011110011000101011000100111001011",
                "011010001101011110011011111010111110010100101000110111010110",
                "001110000111100100101110001011101010001100010111110111011011",
                "111100001000001100010110101100111001001111100100110000001101",
                "001110010000000111011110000011000010101000111000000110101101",
                "100100011101011111001101001010011111110010111101000010000111",
                "110010100110101100001101111101010011000110101100000110001010",
                "110101101100001110000100010001001010100010110100100001000011",
                "100100000100001101010101001101000101101000000101111110001010",
                "101101011010101000111110110000110100000010011111111100110010",
                "101111000100000100011000010001011111001010010001010110001010",
                "001010001110101010000100010011101001010101101101010111100101",
                "001111110000101100010111111100000100101010000001011101100001",
                "101011110010000010010110000100001010011111100011011000110010",
                "011110010100011101100101111101000001011100001011010001110011",
                "000101000101000010010010110111000010101111001101100110011100",
                "100011100110011111000110011001111100001110110111001001000111",
                "111011000110001000110111011001011110010010010110101000011111",
                "011110011110110110011011001011010000100100101010110000010011",
                "010011110011100101010101111010001001001111101111101110011101"
        };
        for(int i = 0;i < 30;i ++) {
            for(int j = 0;j < 60;j ++) {
                g[i][j] = s[i].charAt(j);
            }
        }
        // 找一个可以切入的点
        int ans = 0;
        for(int i = 0;i < 30;i ++) {
            for(int j = 0;j < 60;j ++) {
                cnt = 0;
                if (g[i][j] == '1' && !b[i][j]) {
                    dfs (i,j);
                }
                ans = Math.max(ans,cnt);
            }
        }
        System.out.println(ans);    // ans = 148

    }
    public static void dfs (int x, int y) {
        cnt ++;
        if (x < 0 || x >= 30 || y < 0 || y >= 60 || g[x][y] == '0' || b[x][y]) {
            return; // 不满足dfs条件
        }
        // 这就可以开始正式的dfs
        b[x][y] = true;
        // 遍厉四个方向,看看有没有可通的路
        for(int i = 0;i < 4;i ++) {
            int tx = x + f[i][0];
            int ty = y + f[i][1];
            if (tx >= 0 && tx < 30 && ty >= 0 && ty < 60 && g[tx][ty] == '1' && !b[tx][ty]) {
                dfs (tx , ty);
            }
        }
    }
}

五子棋对弈

链接:https://www.lanqiao.cn/problems/19694/learning/=

 问题描述

"在五子棋的对弈中,友谊的小船说翻就翻?" 不!对小蓝和小桥来说,五子棋不仅是棋盘上的较量,更是心与心之间的沟通。这两位挚友秉承着"友谊第一,比赛第二"的宗旨,决定在一块 5×5 的棋盘上,用黑白两色的棋子来决出胜负。但他们又都不忍心让对方失落,于是决定用一场和棋(平局)作为彼此友谊的见证。

比赛遵循以下规则:

  1. 棋盘规模:比赛在一个 5×5 的方格棋盘上进行,共有 25 个格子供下棋使用。
  2. 棋子类型:两种棋子,黑棋与白棋,代表双方。小蓝持白棋,小桥持黑棋。
  3. 先手规则:白棋(小蓝)具有先手优势,即在棋盘空白时率先落子(下棋)。
  4. 轮流落子:玩家们交替在棋盘上放置各自的棋子,每次仅放置一枚。
  5. 胜利条件:率先在横线、竖线或斜线上形成连续的五个同色棋子的一方获胜。
  6. 平局条件:当所有 25 个棋盘格都被下满棋子,而未决出胜负时,游戏以平局告终。

在这一设定下,小蓝和小桥想知道,有多少种不同的棋局情况,既确保棋盘下满又保证比赛结果为平局。

答案提交

这是一道结果填空题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

运行限制

语言最大运行时间最大运行内存
C++1s256M
C1s256M
Java3s512M
Python310s512M
PyPy33s512M
Go5s512M
JavaScript5s512M

思路

        这一题也是矩阵那种的,但是它和上一题最大连通还有点不一样。

        但是我又说不清具体有什么区别。我的理解就是,最大连通那题它dfs路径我们是不可预知的,也就是说它可以随意拐弯,所以我们只有通过设置四个方向去这样遍厉,才能够找到所有可能的结果。

        但是这道题不太一样,因为五子棋对弈这道题它的矩阵大小是5x5,这样五子棋胜利的情况就直接被限定死了,只有竖着5颗同色棋子、横着5颗同色棋子、对角线5颗同色棋子。也就是有这3大类的情况(横、竖、斜),所以我觉得这种类型我们也没有设置方向的必要。我们可以直接按照横、竖、斜三种方向分类就行,判断有没有人获胜就行。那么怎么判断???

        所以这一类的dfs就需要一个check()函数,去判断有没有人获胜。如果check为true,就说明是平局,也有了一种答案;为false就不可以,就说明肯定有人赢了。

        那么如何设计check()?

        因为题目要求是平局,所以我们在dfs中直接判断赢的情况,只要赢了,就直接返回false。只有3大类情况都没有人赢,我们才能返回true,然后我们让答案ans++。

        dfs里面需要check()函数,那么dfs的其他部分该怎么写呢?

        因为题目中白棋先手,所以自始至终棋盘上的白棋数都会比黑棋数多一,因为总共是5x5,所以平局总共就有25个子,易得白棋最多13个,黑棋最多12个。知道黑白棋数的关系,下面我们就该想一想,怎么下棋?

        下棋肯定需要棋盘和棋子的坐标,那么坐标怎么求?

        棋盘我们设为g[][],g = 1为黑子,g = 0为白子。然后下面求每个棋子对应的坐标,设num为棋盘上的总棋子数,所以很容易求得,坐标x为num/5,坐标y为num%5。然后知道坐标之后就可以开始下棋,设白棋数为w,黑棋数为b,如果w < 13,我们就下白子,dfs(num+1,w+1, b);如果b < 12就下黑子,dfs(num + 1, w, b + 1)。两个dfs全部执行完之后,所有可能的棋盘方案数也就枚举完成了。当num == 25的时候,我们就用check()判断一下就行,true就ans++,false就直接return。

五子棋对弈-代码

感觉这题还是讲的不明白,还没理解到精华部分,一表述出来就会冒很多问题。。。。。。

如果看不懂可以直接看代码,代码我觉得还挺好理解的。

import java.util.Scanner;
public class Main {
  static int[][]g = new int[5][5];
  static int ans = 0;
  static boolean[][]b = new boolean[5][5];
  public static void main(String[] args) {
    dfs(0,0,0);
    System.out.println(ans);  // ans = 3126376
  }
  public static void dfs(int num,int w,int b) {   // num棋盘上总棋子数, w白子个数, b黑子个数
    if (num == 25) {
      if (check()) {
        ans ++;
      }
      return;
    }
    // 找到每个棋子对应的坐标
    int x = num / 5;
    int y = num % 5;
    // 开始下棋子
    if (w < 13) { // 白棋先手,所以最终会比黑棋多1个
      g[x][y] = 0;  // 下白子
      dfs(num + 1,w + 1,b);
    }
    if (b < 12) {
      g[x][y] = 1;  // 下黑子
      dfs(num + 1,w,b + 1);
    }
  }
  public static boolean check () {   // 判断是否有人胜出,应该是没人胜出ans才加1
    // 检查棋盘
    // 先判断行、列有没有连成线的
    for(int i = 0;i < 5;i ++) {
      int rcnt = 0; // 统计行上边有没有连成线的
      int ccnt = 0; // 统计列上边有没有连成线的
      for(int j = 0;j < 5;j ++) {
        rcnt += g[i][j];
        ccnt += g[j][i];
      }
      if (rcnt == 0 || ccnt == 5) {
      return false; // 有人胜出
      }
      if (rcnt == 5 || ccnt == 0) {
        return false;
      }
    }
    
    // 判断对角线上边
    int d1 = 0;
    int d2 = 0;
    for(int i = 0;i < 5;i ++) {
      d1 += g[i][i];
      d2 += g[i][4-i];
    }
    if (d1 == 0 || d2 == 5) {
      return false;
    }
    if (d1 == 5 || d2 == 0) {
      return false;
    }
    return true;
  }
}

分糖果

链接:https://www.lanqiao.cn/problems/4124/learning/

问题描述

两种糖果分别有 9 个和 16 个,要全部分给 7 个小朋友,每个小朋友得到的糖果总数最少为 2 个最多为 5 个,问有多少种不同的分法。糖果必须全部分完。

只要有其中一个小朋友在两种方案中分到的糖果不完全相同,这两种方案就算作不同的方案。

答案提交

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

思路

这题我感觉是一维的dfs,甚至都没用到数组。

        题意就是让你把两种糖果分给7个小朋友,每个小朋友只能得到2-5个糖果,且糖果必须全部分完,让你求可行的方案数。

        我感觉这题的代码可能算不上dfs,通俗点来说应该是的完完全全的暴力。

        先看那个dfs()的思路,我们传进去的参数是i,表示在给第i个小朋友分糖果。那么如果i>n(小朋友总数),就说明我们已经给7个小朋友分完糖果了,但是分完糖果就完事了吗?答案是否定的。我们还需判断两种糖果是否有剩余,只有他们全为0的时候,才说明是全分完的,我们才能让ans++。然后我们还要判断是否有越界的,就是糖果不能一直往下减,如果有一个减到0以下了,我们就直接return。最后那个for循环就是dfs的核心,j表示我们给第i个小朋友总共发了j个糖果,然后里面的k表示,第一种糖果我给了第i个小朋友k个,那么第二种糖果很显然就给了j-k个。如果运行完b-=j-k这一行代码,就说明第i个小朋友我们已经完成分配糖果的任务了,所以我们直接再给下一个小朋友i+1进行分配dfs(i+1);但是要注意我们dfs(i+1)之后还要恢复,因为有可能dfs(i+1)是不满足的,所以我们还需要从第i个状态去遍厉dfs另一条分支
        最后输出结果ans就可以了。

分糖果-代码

import java.util.Scanner;

public class Main {
    static int a = 9;
    static int b = 16;
    static int n = 7;
    static int ans = 0;
    public static void main(String[]args) {
        dfs (1);
        System.out.println(ans);
    }
    public static void dfs (int i) {
        if (i > n) {
            if (a == 0 & b == 0) {
                ans ++;
            }
            return;
        }
        if (a < 0 || b < 0) {
            return;
        }
        for(int j = 2;j <= 5;j ++) {
            for(int k = 0;k <= j;k ++) {
                a -= k;
                b -= j - k;
                dfs (i + 1);
                a += k;
                b += j - k;
            }
        }
    }
}

飞机降落

链接:https://www.lanqiao.cn/problems/3511/learning/

问题描述

N 架飞机准备降落到某个只有一条跑道的机场。其中第 i 架飞机在 Ti 时刻到达机场上空,到达时它的剩余油料还可以继续盘旋 Di 个单位时间,即它最早可以于 Ti 时刻开始降落,最晚可以于 Ti+Di 时刻开始降落。降落过程需要 Li 个单位时间。

一架飞机降落完毕时,另一架飞机可以立即在同一时刻开始降落,但是不能在前一架飞机完成降落前开始降落。

请你判断 N 架飞机是否可以全部安全降落。

输入格式

输入包含多组数据。

第一行包含一个整数 T ,代表测试数据的组数。

对于每组数据,第一行包含一个整数 N 。

以下 N 行,每行包含三个整数:Ti ​,Di 和 Li 。

输出格式

对于每组数据,输出 YES  或者 NO ,代表是否可以全部安全降落。

样例输入

2
3
0 100 10
10 10 10
0 2 20
3
0 10 20
10 10 20
20 10 20

样例输出

YES
NO

样例说明

对于第一组数据,可以安排第 3 架飞机于 0 时刻开始降落,20 时刻完成降落。安排第 2 架飞机于 20 时刻开始降落,30 时刻完成降落。安排第 1 架飞机于 30 时刻开始降落,40 时刻完成降落。

对于第二组数据,无论如何安排,都会有飞机不能及时降落。

评测用例规模与约定

对于 30% 的数据,N≤2N≤2。

对于 100% 的数据,1≤T≤10 ,1≤N≤10 ,0≤Ti,Di,Li≤10^5 。

运行限制

语言最大运行时间最大运行内存
C++2s256M
C2s256M
Java3s256M
Python34s256M
PyPy34s256M
Go4s256M
JavaScript4s256M

思路

        这道题其实和五子棋对弈那题有一点点相似,就是它不是传统的那种走迷宫形式的dfs,所以这种题目dfs传进去的参数就比较灵活,也需要我们好好的考虑以下如何进行dfs。

        这道题我一开始做的时候也没有思路,还以为是贪心。后来看了大佬们的题解就恍然大悟,也才意识到dfs传进去的参数是多么的灵活。

        首先这道题的题意就是,根据给你的N架飞机的到达机场时间、盘旋时间以及降落所需时间,去判断,存不存在一种合理的方式,能够让这N架飞机降落成功。所谓的降落成功就是题目中的:一架飞机降落完毕时,另一架飞机可以立即在同一时刻开始降落,但是不能在前一架飞机完成降落前开始降落。

        所以题意还是挺好理解的,下面的重点就是,我们怎么用代码去找合理的降落顺序?

        我们先想一想,我们用什么去衡量能不能让当前飞机开始降落?答案很显然,应当是总时间。就是当前飞机的到达时间 + 当前飞机的盘旋时间 >= 前面飞机降落完成的时间,即总时间为前面飞机降落完成的时间。如果满足这个条件:当前飞机的到达时间 + 当前飞机的盘旋时间 >= 前面飞机降落完成的时间,那么当前飞机是一定可以降落的,只不过当前飞机降落完成的时间会不一定而已。为什么这么说?因为如果当前飞机到达的时候,就已经大于前一个飞机降落完成的时间了,那么这架飞机一到这就可以直接降落,所以这架飞机降落完的时间就是这架飞机的到达时间+当前飞机降落所需时间如果当前飞机到达的时候,前面的飞机正在降落,那么我们就要先盘旋一会,所以最终当前飞机降落完成的时间就是,前一架飞机降落完成的时间+这架飞机的降落时间。所以在dfs传进去的参数中,我们要传一个x,表示这是第几架飞机;还要传一个totalTime,表示总时间。最开始就从dfs(0,0)开始就可以,如果dfs里面的x能够等于N,就说明能够合理安排这N架飞机,返回true,打印YES;反之,返回false,打印NO。

飞机降落-代码

import java.util.Scanner;
public class Main {
  static int N,T;
  static boolean flag ;
  static int[]t;  // 到达机场时间
  static int[]d;  // 盘旋时间
  static int[]l;  // 降落所需时间
  static boolean[]b;
    public static void main(String[] args) {
      Scanner sc = new Scanner(System.in);
      T = sc.nextInt();
      while(T-- > 0) {
        N = sc.nextInt();
        t = new int[N];
        d = new int[N];
        l = new int[N];
        b = new boolean[N];
        for(int i = 0;i < N;i ++) {
          t[i] = sc.nextInt();
          d[i] = sc.nextInt();
          l[i] = sc.nextInt();
        }
        dfs(0,0);
        if (flag) {
          System.out.println("YES");
        }
        else {
          System.out.println("NO");
        }
        flag = false; // 因为是多组输入,最后重置一下flag,设成默认值false
      }
    }
  public static void dfs (int x, int totalTime) {
    if (x == N) {
      flag = true;
      return;
    }
    for(int i = 0;i < N;i ++) {
      if (b[i]) {
        continue;
      }
      b[i] = true;
      if (t[i] + d[i] >= totalTime)  {  // 到达时间 + 盘旋时间 >= 上一架飞机的'完成'降落时间   就可以降落
        if (t[i] > totalTime) {   // 到达时间 > 前一个飞机完成降落的时间
          dfs(x+1,t[i] + l[i]);   // 判断下一架飞机 , 时间更新为飞机x+1到达时间 + 对应的降落时间。因为这种情况它是到这就能飞的,所以直接用到达时间加就可以。
        }
        else if (t[i] <= totalTime) {
          dfs(x+1,totalTime + l[i]);  // 这种就是到达时间 <= 前一个飞机完成降落的时间,所以我们要等前一个飞机飞完,即等到totalTime就可以,然后加上飞机x+1的降落时间即可。
        }
      }
      b[i] = false;  // 回溯取消标记
    }
  }
}
http://www.dtcms.com/a/108404.html

相关文章:

  • RainbowDash 的旅行
  • HCIP的配置
  • 使用Python和OpenCV进行指纹识别与验证
  • 通过必应壁纸官方api实现网页背景图片随机展示
  • substring() 和 slice() 这两个方法的相同与不同
  • Python 脚本:自动化你的日常任务
  • 【element ui】翻页记忆功能、多选时执行删除操作,刷新表格问题
  • VS+Qt配置QtXlsx库实现execl文件导入导出(全教程)
  • 自动调整PPT文本框内容:防止溢出并智能截断文本
  • Shiro学习(三):shiro整合springboot
  • 城电科技 | 探秘零碳校园:创新应用,引领绿色未来
  • PHP在Debian环境上的并发处理能力如何
  • 深度学习处理文本(6)
  • STM32实现一个简单电灯
  • 2023年12月电子学会青少年软件编程四级考级真题—新“跳7”游戏
  • OpenCV
  • 系统与网络安全------Windows系统安全(5)
  • Maya软件中的约束基础:提高角色动画制作效率的关键技术
  • 【解决】Edge浏览器硬件加速问题:无法滚动与卡顿的应对方法
  • Lumerical ------ Edge coupler design
  • Test——BUG篇
  • 掌握 Git 的艺术:Rebase 和 Merge 的使用技巧
  • MySQL 中 LOCK TABLES(手动锁表) 语句的详细说明,包括语法、使用场景、示例代码及注意事项
  • c加加学习之day02
  • ubuntu制做vsftpd的docker镜像
  • git总是链接不成功
  • Excel处理控件Spire.XLS系列教程:C# 打印 Excel 文档
  • 【算法】双指针
  • GIT ---- 解决【fatal: Authentication failed for】
  • 【案例89】达梦数据库优化器参数导致的SQL执行错误