#P2017. 2024.9.7-第3题-粉色星球
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 103
            Accepted: 24
            Difficulty: 7
            
          
          
          
                       所属公司 : 
                              米哈游
                                
            
                        
              时间 :2024年9月7日
                              
                      
          
 
- 
                        算法标签>并查集          
 
2024.9.7-第3题-粉色星球
思路:并查集
进行两次dfs,第一次将所有陆地归到各自的并查集中,并采用路径压缩将并查集中的所有点归到一个点上。
之后,再次进行dfs,扫描所有的海洋,统计与该海洋相邻的并查集的点的个数及该海洋点的个数,更新答案即可。
代码
Java
import java.util.*;
public class Main {
    static int n, m;
    static String[] s;
    static int[] pre, cnt;
    static boolean[][] vis;
    static Set<Integer> sett = new HashSet<>();
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt(); // 读取n
        m = sc.nextInt(); // 读取m
        s = new String[n];
        for (int i = 0; i < n; ++i) {
            s[i] = sc.next(); // 读取每行字符串
        }
        
        pre = new int[n * m];
        cnt = new int[n * m];
        vis = new boolean[n][m];
        for (int i = 0; i < n * m; ++i) {
            pre[i] = i; // 初始化并查集
            cnt[i] = 1; // 初始化每个区域的大小
        }
        // 并查集的find函数
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (!vis[i][j] && s[i].charAt(j) == '#') {
                    dfs(i, j, i * m + j); // 扫描每个连通块
                }
            }
        }
        
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (!vis[i][j] && s[i].charAt(j) == '.') {
                    sett.clear();
                    ans = Math.max(ans, dfs2(i, j)); // 寻找最大的空地
                }
            }
        }
        System.out.println(ans);
    }
    // 并查集的find函数
    static int find(int x) {
        if (x == pre[x]) return x;
        return pre[x] = find(pre[x]); // 路径压缩
    }
    // 深度优先搜索用于标记连通块
    static void dfs(int x, int y, int z) {
        if (x < 0 || x >= n || y < 0 || y >= m || vis[x][y] || s[x].charAt(y) == '.') return;
        int tz = find(x * m + y); // 找到该格子的根
        if (tz != z) {
            pre[tz] = z; // 合并两个区域
            cnt[z] += cnt[tz]; // 更新区域大小
        }
        vis[x][y] = true; // 标记已访问
        dfs(x + 1, y, z); // 向四个方向递归
        dfs(x - 1, y, z);
        dfs(x, y + 1, z);
        dfs(x, y - 1, z);
    }
    // 第二次深度优先搜索用于计算空地的最大值
    static int dfs2(int x, int y) {
        if (x < 0 || x >= n || y < 0 || y >= m) return 0;
        if (s[x].charAt(y) == '#') {
            int z = find(x * m + y); // 找到该格子的根
            if (sett.contains(z)) return 0; // 如果已处理过该连通块则跳过
            sett.add(z); // 添加到集合中
            return cnt[z]; // 返回该连通块的大小
        }
        if (vis[x][y]) return 0; // 如果已访问则跳过
        vis[x][y] = true; // 标记已访问
        int ret = 1; // 计数当前空地
        ret += dfs2(x + 1, y); // 向四个方向递归
        ret += dfs2(x - 1, y);
        ret += dfs2(x, y + 1);
        ret += dfs2(x, y - 1);
        return ret; // 返回总空地大小
    }
}
        题目内容
米小游和小美到达了一个被红色和海洋包裹着的星球,星球上有一些陆地,小美准备点燃其中一大片海。
具体来说,就是在一张二维地图上,分成了n×m个格子,每个格子的类型要么是大海,要么是陆地,若相邻(通过格子的边相邻)两个格子的类型相同,则视为同一个连通块,大海被点燃后会变成陆地,点燃一片大海就是把一个 大海连通块变成一个陆地连通块。
米小游想知道在小美最多点燃一片大海后,最大陆地连通块大小的最大值是多少。
输入描述
第一行输入两个整数n,m(1≤n,m≤1000),表示地图大小。
接下来n行,每行输入一个长度为m的字符串s表示地图,其中'_'表示这个格子是大海,‘#’表示这个格子是陆地。
输出描述
输出一个整数表示答案
样例1
输入
5 5
##..#
#..#.
###..
.#.#.
#.#..
输出
17
说明
点燃右下角的那一片大海,可以发现,点燃其他大海后的效果没有更优。