【888题秋招篇】剑指大厂offer第八题,带你用深度优先搜索秒杀米哈游校招真题-RGB矩阵,斩获大厂年薪60wOffer

更多精彩内容

这里是带你游历编程世界的Dashcoding编程社,我是Dash/北航硕士/ICPC区域赛全国排名30+/给你呈现我们眼中的世界!

256题算法特训课,帮你斩获大厂60W年薪offer

原题

米哈游校招真题RGB矩阵

B站动画详解

问题分析

这道题目要求我们计算在一个由红色(R)、绿色(G)和蓝色(B)三种颜色组成的矩阵中,由于小明的视觉缺陷导致他无法区分蓝色和绿色,进而影响他对矩阵连通块数量的判断。具体来说,连通块是指颜色相同且上下左右相邻的格子集合。我们需要计算小明视角下的连通块数量与实际连通块数量的差值。

解决这个问题的思路可以分为两步:

  1. 计算实际的连通块数量:首先,在不考虑小明的视觉缺陷情况下,使用深度优先搜索(DFS)算法遍历整个矩阵,统计实际的连通块数量。
  2. 模拟小明的视角:由于小明无法区分绿色和蓝色,我们将矩阵中所有的绿色(G)转换为蓝色(B),然后再次使用DFS统计在这种情况下的连通块数量。通过比较实际的连通块数量和小明视角下的连通块数量,我们可以得到小明视角比实际少的连通块数量。

深度优先搜索(DFS)适合这种图遍历问题,它通过递归的方式遍历所有相邻的相同颜色的格子,直到整个连通块被完全标记为已访问。该算法能够保证所有连通块被正确识别并统计。

思路分析

在这个问题中,我们需要进行两次深度优先搜索(DFS)来解决问题。首先,遍历整个矩阵,对每一个尚未访问的格子进行DFS,将该格子所在的整个连通块标记为已访问,并统计连通块的数量。这一步统计的是实际情况下的连通块数量。

然后,我们将所有的绿色(G)格子转换为蓝色(B),再进行一次DFS遍历,计算小明视角下的连通块数量。因为在小明的视角中,绿色和蓝色无法区分,所以在第二次遍历时,这两种颜色会被视为同一种颜色,导致连通块数量减少。

具体的DFS过程是从一个未访问的格子出发,递归地访问与该格子上下左右相邻且颜色相同的所有格子,直到整个连通块被标记为已访问。每次找到一个新的未访问格子时,就意味着发现了一个新的连通块。

最后,将实际的连通块数量减去小明视角下的连通块数量,即为答案。

算法实现

  1. dxdy 数组表示上下左右四个方向的位移,用于在 DFS 中扩展到相邻格子。
  2. dfs 函数是深度优先搜索的核心,通过递归访问所有相邻且未访问的相同颜色的格子。
  3. query 函数遍历整个矩阵,统计未访问的连通块数量,每次发现一个新的连通块时调用 dfs 进行标记。
  4. 主函数中首先进行第一次 query 来统计实际的连通块数量。然后将所有的绿色格子转换为蓝色,再进行一次 query 来统计小明视角下的连通块数量,最后输出两者的差值。

代码详解

标准代码程序

C++代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1010;
int n, m;
char a[N][N];
bool used[N][N];
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};

// 深度优先搜索函数
void dfs(int x, int y, char c) {
    used[x][y] = true;
    for (int k = 0; k < 4; k++) {
        int i = x + dx[k], j = y + dy[k];
        if (i < 0 || i >= n || j < 0 || j >= m || used[i][j] || a[i][j] != c)
            continue;
        dfs(i, j, c);
    }
}

// 计算连通块数量
int query() {
    int num = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (!used[i][j]) {
                dfs(i, j, a[i][j]);
                num++;
            }
        }
    }
    return num;
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++)
        cin >> a[i];
    
    int num1 = query();  // 实际连通块数量
    
    // 重置used数组
    memset(used, 0, sizeof(used));
    
    // 将所有绿色格子转换为蓝色
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (a[i][j] == 'G')
                a[i][j] = 'B';
        }
    }
    
    int num2 = query();  // 小明视角下的连通块数量
    
    // 输出实际连通块数量与小明视角下连通块数量的差值
    cout << num1 - num2;
    return 0;
}

Java代码

import java.util.Scanner;

public class ColorBlindness {
    static int n, m;
    static char[][] a;
    static boolean[][] used;
    static int[] dx = {1, -1, 0, 0};
    static int[] dy = {0, 0, 1, -1};

    public static void dfs(int x, int y, char c) {
        used[x][y] = true;
        for (int k = 0; k < 4; k++) {
            int i = x + dx[k], j = y + dy[k];
            if (i < 0 || i >= n || j < 0 || j >= m || used[i][j] || a[i][j] != c)
                continue;
            dfs(i, j, c);
        }
    }

    public static int query() {
        int num = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (!used[i][j]) {
                    dfs(i, j, a[i][j]);
                    num++;
                }
            }
        }
        return num;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        m = scanner.nextInt();
        a = new char[n][m];
        used = new boolean[n][m];
        for (int i = 0; i < n; i++) {
            a[i] = scanner.next().toCharArray();
        }

        int num1 = query();

        // Reset used array
        used = new boolean[n][m];

        // Convert all 'G' to 'B'
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (a[i][j] == 'G')
                    a[i][j] = 'B';
            }
        }

        int num2 = query();

        System.out.println(num1 - num2);
    }
}

Python代码

def dfs(x, y, c):
    used[x][y] = True
    for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
        i, j = x + dx, y + dy
        if 0 <= i < n and 0 <= j < m and not used[i][j] and a[i][j] == c:
            dfs(i, j, c)

def query():
    num = 0
    for i in range(n):
        for j in range(m):
            if not used[i][j]:
                dfs(i, j, a[i][j])
                num += 1
    return num

n, m = map(int, input().split())
a = [list(input()) for _ in range(n)]
used = [[False] * m for _ in range(n)]

num1 = query()

# Reset used array
used = [[False] * m for _ in range(n)]

# Convert all 'G' to 'B'
for i in range(n):
    for j in range(m):
        if a[i][j] == 'G':
            a[i][j] = 'B'

num2 = query()

print(num1 - num2)

Javascript代码

const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

let n, m;
let a = [];
let used = [];
const dx = [1, -1, 0, 0];
const dy = [0, 0, 1, -1];

function dfs(x, y, c) {
    used[x][y] = true;
    for (let k = 0; k < 4; k++) {
        let i = x + dx[k], j = y + dy[k];
        if (i >= 0 && i < n && j >= 0 && j < m && !used[i][j] && a[i][j] === c) {
            dfs(i, j, c);
        }
    }
}

function query() {
    let num = 0;
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < m; j++) {
            if (!used[i][j]) {
                dfs(i, j, a[i][j]);
                num++;
            }
        }
    }
    return num;
}

rl.on('line', (line) => {
    if (!n) {
        [n, m] = line.split(' ').map(Number);
    } else {
        a.push(line.split(''));
        if (a.length === n) {
            used = Array.from({ length: n }, () => Array(m).fill(false));
            const num1 = query();

            // Reset used array
            used = Array.from({ length: n }, () => Array(m).fill(false));

            // Convert all 'G' to 'B'
            for (let i = 0; i < n; i++) {
                for (let j = 0; j < m; j++) {
                    if (a[i][j] === 'G') a[i][j] = 'B';
                }
            }

            const num2 = query();
            console.log(num1 - num2);
            rl.close();
        }
    }
});

复杂度分析

时间复杂度

在这道题中,主要的操作集中在深度优先搜索(DFS)和遍历矩阵上。我们可以通过分析以下步骤来得出时间复杂度:

  1. DFS 操作: DFS 的复杂度为 O ( N × M ) O(N \times M) O(N×M),其中 N N N M M M 分别表示矩阵的行数和列数。每次 DFS 将访问一个新的连通块中的所有格子,因此最多访问整个矩阵中的所有格子一次。

  2. 两次遍历操作: 题目要求我们进行两次连通块统计。第一次统计时按照实际颜色分布,第二次统计时将所有的绿色(G)转换为蓝色(B)后再进行统计。这两次遍历都涉及到 O ( N × M ) O(N \times M) O(N×M) 的复杂度。

综上所述,总的时间复杂度为 O ( N × M ) O(N \times M) O(N×M)。由于题目中的 N N N M M M 的最大值为 1000,因此算法可以有效处理最大规模的输入。

空间复杂度

空间复杂度主要由以下几个部分组成:

  1. 存储矩阵的空间: 我们使用一个大小为 N × M N \times M N×M 的二维数组来存储矩阵,因此空间复杂度为 O ( N × M ) O(N \times M) O(N×M)

  2. 标记访问情况的空间: 我们需要一个与矩阵相同大小的二维数组 used 来记录每个格子是否已经访问过,因此空间复杂度同样为 O ( N × M ) O(N \times M) O(N×M)

  3. 递归栈的空间: DFS 的递归深度与矩阵的大小有关,最坏情况下,递归深度为 O ( N × M ) O(N \times M) O(N×M),但通常情况下,递归深度要远小于矩阵大小。

因此,总的空间复杂度为 O ( N × M ) O(N \times M) O(N×M)

总结

这道题目通过模拟小明在面对颜色区分困难时的视角变化,考察了我们对于图遍历和连通块统计的理解。题目中的核心是通过深度优先搜索(DFS)算法遍历整个矩阵,以统计连通块的数量。为了模拟小明的视觉缺陷,我们将绿色(G)转换为蓝色(B),从而使得小明视角下的连通块数量减少。最后通过两次连通块统计,我们计算出实际连通块数量与小明视角下连通块数量的差值,进而得到答案。

这道题目较好地结合了图论中的基础概念(如连通块)和搜索算法(DFS),其解决思路简洁明了,适合在复杂度较大的矩阵中应用。在处理矩阵类问题时,DFS 是一种有效且常用的遍历策略。通过合理的递归和边界处理,DFS 可以帮助我们快速解决这类涉及连通性的题目。算法的时间复杂度为 O ( N × M ) O(N \times M) O(N×M),能够高效应对输入规模较大的情况,而空间复杂度为 O ( N × M ) O(N \times M) O(N×M),主要用于存储矩阵和标记访问的状态。

  • 11
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
米哈游Java后端笔试真题是面向Java后端开发岗位的一套笔试目,旨在考察应聘者对Java后端开发的理解和应用能力。 这套笔试目往往包含以下几个方面的内容: 1. Java语言基础:主要考察Java的基本语法、面向对象编程、常用类库等方面的知识。例如,常见的有关Java语言基础的目有:反射机制的理解和应用、线程的创建和使用、异常处理等。 2. 数据库相关知识:主要考察应聘者对数据库的基本理解和SQL的使用能力。例如,常见的数据库相关目有:数据库事务的概念和应用、数据库索引的优化策略、SQL语句的编写和调优等。 3. Web开发框架:主要考察应聘者对常用的Web开发框架的理解和应用能力。例如,常见的Web开发框架目有:Spring框架的注解使用、MyBatis的配置和映射关系等。 4. 系统设计和优化:主要考察应聘者对大型系统设计和性能优化的能力。例如,常见的系统设计和优化目有:数据库连接池的设计和实现、分布式系统的负载均衡策略、系统性能调优等。 总的来说,米哈游Java后端笔试真题是一套综合考察Java后端开发能力的目,内容涵盖了Java语言基础、数据库相关知识、Web开发框架以及系统设计和优化等方面。应聘者需要具备扎实的Java编程基础,熟悉常用的开发框架和工具,以及对大型系统的设计和性能优化有一定的了解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值