1 条题解

  • 0
    @ 2025-5-7 20:21:06

    题解

    这道题目要求我们在一个矩阵中找到多个香蕉树区域,并计算出通过连接 k 个区域能够得到的最多香蕉树数量。每个区域是由相邻的香蕉树组成的,区域内的香蕉树通过水平或垂直相邻的关系形成连通块。通过连接这些区域,可以帮助猴子获得更多的香蕉。

    问题分析

    1. 区域的定义

      • 每个区域是由一组相邻的香蕉树组成的,可以通过连接相邻的香蕉树来形成一个区域。
      • 相邻的香蕉树可以通过水平或垂直的方向连通。
    2. 目标

      • 给定 k 个区域,目标是选择这些区域中含有最多香蕉树的 k 个区域,并输出这些区域的香蕉树数量之和。
    3. 解法思路

      • 步骤1:首先根据给定的香蕉树的位置,找到所有的连通区域。每个区域由一组相邻的香蕉树组成。
      • 步骤2:通过并查集(Union-Find)算法,合并相邻的香蕉树,找到所有的连通区域。
      • 步骤3:统计每个区域内的香蕉树数量,并将这些数量按从大到小排序。
      • 步骤4:选择最多的 k 个区域,求出它们的香蕉树总数。

    代码实现

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    #include <cstring>
    using namespace std;
    
    #define MAXN 16010
    
    // 定义每个香蕉树节点的结构
    struct Node {
        int x, y;  // 香蕉树的坐标
        int num;   // 每个香蕉树的编号
    } arr[MAXN];
    
    int b[MAXN], result[MAXN];  // b 用于并查集,result 存储每个区域的香蕉树数量
    
    // 按照 x 坐标排序
    bool compareX(Node &a, Node &b) {
        if (a.x != b.x) {
            return a.x < b.x;
        }
        return a.y < b.y;
    }
    
    // 按照 y 坐标排序
    bool compareY(Node &a, Node &b) {
        if (a.y != b.y) {
            return a.y < b.y;
        }
        return a.x < b.x;
    }
    
    // 排序用的比较函数,按香蕉树数量从大到小排序
    bool compare(int a, int b) {
        return a > b;
    }
    
    // 查找并查集的根节点
    int seek(int x) {
        if (x == b[x])
            return x;
        return b[x] = seek(b[x]);  // 路径压缩
    }
    
    // 连接两个元素
    int setting(int x, int y) {
        x = seek(x);
        y = seek(y);
        if (x == y)
            return 0;
        else
            b[x] = y;  // 合并两个集合
        return 0;
    }
    
    int main() {
        int n, m, ans = 0;
        
        // 输入香蕉树的数量和需要连接的区域数量
        scanf("%d%d", &n, &m);
        
        // 初始化并查集
        for (int i = 0; i < n; i++) {
            b[i] = i;  // 初始时每个香蕉树是一个单独的区域
            scanf("%d%d", &arr[i].x, &arr[i].y);  // 输入香蕉树的坐标
            arr[i].num = i + 1;  // 编号从1开始
        }
        
        // 按照 x 坐标排序
        sort(arr, arr + n, compareX);
        for (int i = 0; i < n - 1; i++) {
            if (arr[i].x == arr[i + 1].x && arr[i + 1].y - arr[i].y == 1) {
                setting(arr[i].num, arr[i + 1].num);  // 连接相邻的香蕉树
            }
        }
    
        // 按照 y 坐标排序
        sort(arr, arr + n, compareY);
        for (int i = 0; i < n - 1; i++) {
            if (arr[i].y == arr[i + 1].y && arr[i + 1].x - arr[i].x == 1) {
                setting(arr[i].num, arr[i + 1].num);  // 连接相邻的香蕉树
            }
        }
        
        // 每个香蕉树都找到它所在的区域
        for (int i = 0; i < n; i++) {
            b[i] = seek(i);  // 路径压缩,找到根节点
        }
        
        // 统计每个区域内的香蕉树数量
        for (int i = 0; i < n; i++) {
            result[b[i]]++;  // 统计每个区域的大小
        }
        
        // 按区域的香蕉树数量从大到小排序
        sort(result, result + n + 1, compare);
    
        // 选择最大的 m 个区域,求它们的香蕉树总数
        for (int i = 0; i < m; i++) {
            ans += result[i];  // 累加最大的 m 个区域的数量
        }
    
        // 输出结果
        printf("%d\n", ans);
        return 0;
    }
    

    代码解释

    1. 输入解析

      • n 表示香蕉树的数量,m 表示需要连接的区域数量。
      • arr[i].xarr[i].y 存储每棵香蕉树的坐标,arr[i].num 存储每棵树的编号。
    2. 并查集操作

      • 使用并查集(Union-Find)来合并相邻的香蕉树。在相邻的香蕉树之间建立联系,通过 setting 函数来进行连接。
      • seek 函数用于查找一个元素的根节点,路径压缩技术可以优化查找过程。
    3. 排序与连接

      • 我们通过两次排序来连接相邻的香蕉树:第一次按 x 坐标排序,连接水平相邻的香蕉树;第二次按 y 坐标排序,连接垂直相邻的香蕉树。
    4. 统计每个区域的大小

      • 在并查集操作后,每棵树都指向它所在的区域的根节点。我们统计每个区域内的香蕉树数量并存储在 result 数组中。
    5. 选择最大的 k 个区域

      • 排序所有区域的香蕉树数量,选择最大的 k 个区域,计算它们的香蕉树总数。
    6. 输出结果

      • 输出选择的 k 个区域的香蕉树总数。

    复杂度分析

    • 时间复杂度

      • 排序操作的时间复杂度是 O(nlogn)O(n \log n),并查集操作的时间复杂度是 O(α(n))O(\alpha(n)),其中 α(n)\alpha(n) 是反阿克曼函数,接近常数。
      • 因此,总的时间复杂度是 O(nlogn)O(n \log n),在 n16000n \leq 16000 的范围内是可以接受的。
    • 空间复杂度

      • 我们使用了几个数组:arr 存储香蕉树的位置和编号,b 存储并查集的父节点,result 存储每个区域的大小。空间复杂度是 O(n)O(n)

    示例

    输入:

    10 3
    7 10
    1 1
    101 1
    2 2
    102 1
    7 11
    200 202
    2 1
    3 2
    103 1
    

    输出:

    9
    

    结论

    这道题目通过并查集的方式高效地找到了所有连通的香蕉树区域,并通过动态选择最大区域来求解答案。通过合理地使用并查集和排序,能够在较大的输入规模下得到正确的结果。

    • 1

    信息

    ID
    839
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    2
    已通过
    1
    上传者