1. 程式人生 > 其它 >AcWing第51場周賽——T2(4420. 連通分量)

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、維護連通分量,然後列舉每一個星號,且求出該星號的四個方向的連通分量的數量和(最後不要忘了加1本身)
2、利用一個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/