《魔幻世界中的安全区计算》和《小C的mex查询》| 豆包MarsCode AI刷题

146 阅读4分钟

今天我们将在豆包MarsCode AI刷题平台上,完成《魔幻世界中的安全区计算》与《小C的mex查询》这两个算法问题,通过这些练习提升用户解决此类问题的能力

《魔幻世界中的安全区计算》题面如下:

图片.png

问题理解

很常规的图遍历问题,不知道为什么难度标的是困难;需要计算在一个 n x m 的二维数组中,有多少个由相邻的、危险程度小于等于 X 的格子组成的安全区。

数据结构选择

使用二维数组 a 来表示异世界的危险程度,并使用深度优先搜索(DFS)来遍历和标记安全区。

算法步骤

  1. 初始化:定义一个 color 变量,用于标记不同的安全区。初始值为 -1,每发现一个新的安全区,color 减一。
  2. 遍历二维数组:对于每个格子,如果其危险程度在 [0, X] 范围内,则将其标记为当前的 color,并使用 DFS 递归地标记与之相邻的所有安全格子。
  3. DFS 函数dfs 函数用于递归地标记相邻的安全格子。它检查当前格子的四个方向(上下左右),如果相邻格子是安全的,则继续递归标记。
  4. 计算安全区数量:最终,color 的绝对值减一即为安全区的数量。

具体实现

#include <iostream>
#include <vector>
#include <string>

using namespace std;

int path[4][2] = {{0,1}, {0,-1}, {1,0}, {-1,0}};
void dfs(int n, int m, int X, std::vector<std::vector<int>>& a, int x,int y, int color){
    for(int i=0;i<4;i++){
        int nx = x + path[i][0], ny = y + path[i][1];
        if(nx<0 || nx>=n || ny<0 || ny>=m){
            continue;
        }
        if(a[nx][ny] < 0 || a[nx][ny]>X){
            continue;
        }
        a[nx][ny] = color;
        dfs(n,m,X,a,nx,ny,color);
    }
}
int solution(int n, int m, int X, std::vector<std::vector<int>>& a) {
    int color = -1;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(a[i][j] >=0 && a[i][j]<=X){
                a[i][j] = color;
                dfs(n,m,X,a,i,j,color);
                color--;
            }
        }
    }
    return -color-1;  // 返回安全区的数量
}

int main() {
    std::vector<std::vector<int>> a1 = {{2, 3, 3}, {3, 3, 3}, {3, 3, 3}};
    std::vector<std::vector<int>> a2 = {{6, 6}, {6, 4}};
    std::vector<std::vector<int>> a3 = {{1, 2, 2}, {2, 3, 3}, {3, 4, 5}};
    
    std::cout << (solution(3, 3, 4, a1) == 1) << std::endl;
    std::cout << (solution(2, 2, 5, a2) == 1) << std::endl;
    std::cout << (solution(3, 3, 3, a3) == 1) << std::endl;
    
    return 0;
}

《小C的mex查询》题面如下:

图片.png

问题理解

题目要求对一个空集进行多次操作,每次操作是将一个区间 [l, r] 内的所有整数添加到集合中,并在每次操作后输出当前集合的 mex(即集合中最小的未出现的非负整数)。

算法与数据结构选择

首先这道题当然可以选择使用线段树来实现,但是线段树的代码比较多,为了偷懒,这次我们选择使用bitset这个数据结构实现;bitset 是 C++ 标准库提供的一种固定大小的位向量。每个位可以是 0 或 1,适用于位级操作。bitset 提供了多种位操作方法,如按位与、按位或、按位异或等。

关键操作及复杂度分析

  1. 区间更新

    • 使用位移操作一次性更新区间 [l, r]

    • 具体操作:bits |= bitset<MAXN>((1ull << (r - l + 1)) - 1) << l;

    • 复杂度分析:

      • 生成一个由 lr 位全部为 1 的 bitsetbitset<MAXN>((1ull << (r - l + 1)) - 1) 的复杂度是 O(1),因为bitset 的按位操作都是常数时间。
    • 因此,区间更新的总复杂度是 O(q)。

  2. 计算 mex

    • 由 0 开始,找到第一个未被标记的数字。

    • 具体操作:while(bits.test(curMex)){ curMex++; }

    • 复杂度分析:

      • current_mex 最多需要由 0 一直增加到 MAXN,复杂度为 O(MAXN)。

具体实现

#include <bits/stdc++.h>
#include <bitset>
#include <vector>

using namespace std;

const int MAXN = 1000006;

vector<int> solution(int qLen, vector<vector<int>> queries) {
    // write code here
    vector<int> ret;
    bitset<MAXN> bits;
    int curMex=0;
    for(auto& q : queries){
        bits |= bitset<MAXN>((1ull << (q[1] - q[0]+1)) - 1) << q[0];
        while(bits.test(curMex)){
            curMex++;
        }
        ret.push_back(curMex);
    }
    return ret; // Placeholder
}

int main() {
    cout << (solution(4, {{1, 3}, {7, 8}, {0, 5}, {3, 6}}) == vector<int>{0, 0, 6, 9}) << endl;
    cout << (solution(3, {{0, 2}, {3, 4}, {6, 10}}) == vector<int>{3, 5, 5}) << endl;
    cout << (solution(2, {{2, 5}, {7, 9}}) == vector<int>{0, 0}) << endl;
    return 0;
}

总体复杂度

  • 处理 q 次查询

    • 每次查询的区间更新复杂度是 O(1),总体处理查询的复杂度为O(q)。
  • 计算mex

    • 总体计算 mex 的复杂度在最坏情况下是 O(MAXN)。
  • 总体复杂度:O(q + MAXN)

借助豆包MarsCode AI刷题平台,我们不仅高效地解决了《魔幻世界中的安全区计算》和《小C的mex查询》,还加深了对相关算法和数据结构的理解,后续会借助豆包MarsCode AI给大家展示更多题目的解法。