AcWing第51场周赛——T2(4420. 连通分量)
题目描述
给定一个 n×m 的方格矩阵,每个方格要么是空格(用 . 表示),要么是障碍物(用 * 表示)。
如果两个空格存在公共边,则两空格视为相邻。
我们称一个不可扩展的空格集合为连通分量,如果集合中的任意两个空格都能通过相邻空格的路径连接。
这其实是一个典型的众所周知的关于连通分量(Connected Component )的定义。
现在,我们的问题如下:
对于每个包含障碍物的单元格 (x,y),假设它是一个空格(所有其他单元格保持不变)的前提下,请你计算包含 (x,y) 的连通分量所包含的单元格数量。
注意,所有假设求解操作之间都是相互独立的,互不影响。
输入格式
第一行包含两个整数 n,m。
接下来 n 行,每行包含 m 个字符:. 表示空格,* 表示障碍物。
输出格式
输出一个 n 行 m 列的字符矩阵,其中第 i 行第 j 列的字符对应给定矩阵中第 i 行第 j 列的单元格。如果该单元格为空格,则输出字符为 .,如果该单元格为障碍物,则输出字符为假设该单元格为空格的前提下,包含该单元格的连通分量所包含的单元格数量对 10 取模后的结果。具体格式可参照输出样例。
数据范围
前 5 个测试点满足 1≤n,m≤10
所有测试点满足 1≤n,m≤1000
输入样例1:
3 3
*.*
.*.
*.*
输出样例1:
3.3
.5.
3.3
输入样例2:
4 5
**..*
..***
.*.*.
*.*.*
输出样例2:
46..3
..732
.6.4.
5.4.3
解题思路
并查集
- 维护连通分量,然后枚举每一个星号,且求出该星号的四个方向的连通分量的数量和(最后不要忘了加1本身)
- 利用一个get方法将二维数组点的坐标映射成一个数(作为自己的父节点)——俗称矩阵展开
- 因为打印的数据有点多,所以这里还需要使用缓冲流进行输入输出的优化
Java代码
1 import java.util.*; 2 import java.io.*; 3 4 /** 5 * 并查集 6 */ 7 public class Main { 8 9 static int N = 1000 + 10; 10 static int[] p = new int[N * N]; 11 static int[] s = new int[N * N]; 12 static int[][] g = new int[N][N]; 13 static int n, m; 14 static int[] dx = {0, 1, 0, -1}; 15 static int[] dy = {1, 0, -1, 0}; 16 static int MOD = 10; 17 18 public static int find(int x) { 19 if (p[x] != x) { 20 p[x] = find(p[x]); 21 } 22 return p[x]; 23 } 24 25 public static void init() { 26 for (int i = 1; i <= n; i++) { 27 for (int j = 1; j <= m; j++) { 28 int a = get(i, j); 29 p[a] = a; 30 s[a] = 1; 31 } 32 } 33 } 34 35 /** 36 * 映射二维矩阵中的横纵坐标 37 * 38 */ 39 public static int get(int x, int y) { 40 // 矩阵展开 41 return x * m + y; 42 } 43 44 public static void main(String[] args) throws Exception { 45 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 46 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); 47 String[] str = br.readLine().split(" "); 48 n = Integer.parseInt(str[0]); 49 m = Integer.parseInt(str[1]); 50 51 init(); 52 53 for (int i = 1; i <= n; i++) { 54 char[] c = br.readLine().toCharArray(); 55 for (int j = 1 ; j <= m; j++) { 56 g[i][j] = c[j - 1]; 57 } 58 } 59 60 // 预处理连通块 61 for (int i = 1; i <= n; i++) { 62 for (int j = 1; j <= m; j++) { 63 if (g[i][j] == '.') { 64 // 枚举四个方向 65 for (int k = 0; k < 4; k++) { 66 int x = i + dx[k]; 67 int y = j + dy[k]; 68 if (x >= 1 && x <= n && y >= 1 && y <= m && g[x][y] == '.') { 69 int a = get(i, j); 70 int b = get(x, y); 71 a = find(a); 72 b = find(b); 73 if (a != b) { 74 p[a] = b; 75 s[b] += s[a]; 76 } 77 } 78 } 79 } 80 } 81 } 82 83 // 枚举所有. 84 for (int i = 1; i <= n; i++) { 85 for (int j = 1; j <= m; j++) { 86 if (g[i][j] == '.') { 87 bw.write("."); 88 } else { 89 // 先将四个方向的祖先存储起来,然后再去重 90 TreeSet<Integer> fathers = new TreeSet<>(); 91 for (int k = 0; k < 4; k++) { 92 int x = i + dx[k]; 93 int y = j + dy[k]; 94 if (x >= 1 && x <= n && y >= 1 && y <= m && g[x][y] == '.') { 95 int a = get(x, y); 96 fathers.add(find(a)); 97 } 98 } 99 int sum = 1; 100 while (fathers.size() > 0) { 101 sum += s[fathers.pollFirst()]; 102 } 103 bw.write(String.valueOf(sum % MOD)); 104 } 105 } 106 bw.newLine(); 107 } 108 109 bw.flush(); 110 bw.close(); 111 br.close(); 112 } 113 114 }
Acwing链接:https://www.acwing.com/problem/content/description/4423/