Skip to content

Commit 51473be

Browse files
committed
[Gold III] Title: 마법사 상어와 파이어스톰, Time: 320 ms, Memory: 79712 KB -BaekjoonHub
1 parent bc6d658 commit 51473be

2 files changed

Lines changed: 203 additions & 0 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# [Gold III] 마법사 상어와 파이어스톰 - 20058
2+
3+
[문제 링크](https://www.acmicpc.net/problem/20058)
4+
5+
### 성능 요약
6+
7+
메모리: 79712 KB, 시간: 320 ms
8+
9+
### 분류
10+
11+
구현, 그래프 이론, 그래프 탐색, 시뮬레이션, 너비 우선 탐색, 깊이 우선 탐색
12+
13+
### 제출 일자
14+
15+
2025년 9월 2일 15:38:20
16+
17+
### 문제 설명
18+
19+
<p>마법사 상어는 <a href="/problem/20056">파이어볼</a>과 <a href="/problem/20057">토네이도</a>를 조합해 파이어스톰을 시전할 수 있다. 오늘은 파이어스톰을 크기가 2<sup>N</sup> × 2<sup>N</sup>인 격자로 나누어진 얼음판에서 연습하려고 한다. 위치 (r, c)는 격자의 r행 c열을 의미하고, A[r][c]는 (r, c)에 있는 얼음의 양을 의미한다. A[r][c]가 0인 경우 얼음이 없는 것이다.</p>
20+
21+
<p>파이어스톰을 시전하려면 시전할 때마다 단계 L을 결정해야 한다. 파이어스톰은 먼저 격자를 2<sup>L</sup> × 2<sup>L</sup> 크기의 부분 격자로 나눈다. 그 후, 모든 부분 격자를 시계 방향으로 90도 회전시킨다. 이후 얼음이 있는 칸 3개 또는 그 이상과 인접해있지 않은 칸은 얼음의 양이 1 줄어든다. (r, c)와 인접한 칸은 (r-1, c), (r+1, c), (r, c-1), (r, c+1)이다. 아래 그림의 칸에 적힌 정수는 칸을 구분하기 위해 적은 정수이다.</p>
22+
23+
<table class="table table-bordered td-center td-middle" style="width:100%;">
24+
<tbody>
25+
<tr>
26+
<td><img alt="" src="https://upload.acmicpc.net/68137f5d-fdbd-48c6-92f0-0a74ee53b0c2/-/preview/" style="width: 300px; height: 302px;"></td>
27+
<td><img alt="" src="https://upload.acmicpc.net/4216e4de-a9f7-4bf0-9385-e20c583c1228/-/preview/" style="width: 300px; height: 302px;"></td>
28+
<td><img alt="" src="https://upload.acmicpc.net/a58a4219-afc7-4f77-a194-a5495882eeb4/-/preview/" style="width: 300px; height: 300px;"></td>
29+
</tr>
30+
<tr>
31+
<td>마법을 시전하기 전</td>
32+
<td>L = 1</td>
33+
<td>L = 2</td>
34+
</tr>
35+
</tbody>
36+
</table>
37+
38+
<p>마법사 상어는 파이어스톰을 총 Q번 시전하려고 한다. 모든 파이어스톰을 시전한 후, 다음 2가지를 구해보자.</p>
39+
40+
<ol>
41+
<li>남아있는 얼음 A[r][c]의 합</li>
42+
<li>남아있는 얼음 중 가장 큰 덩어리가 차지하는 칸의 개수</li>
43+
</ol>
44+
45+
<p>얼음이 있는 칸이 얼음이 있는 칸과 인접해 있으면, 두 칸을 연결되어 있다고 한다. 덩어리는 연결된 칸의 집합이다.</p>
46+
47+
### 입력
48+
49+
<p>첫째 줄에 N과 Q가 주어진다. 둘째 줄부터 2<sup>N</sup>개의 줄에는 격자의 각 칸에 있는 얼음의 양이 주어진다. r번째 줄에서 c번째 주어지는 정수는 A[r][c] 이다.</p>
50+
51+
<p>마지막 줄에는 마법사 상어가 시전한 단계 L<sub>1</sub>, L<sub>2</sub>, ..., L<sub>Q</sub>가 순서대로 주어진다.</p>
52+
53+
### 출력
54+
55+
<p>첫째 줄에 남아있는 얼음 A[r][c]의 합을 출력하고, 둘째 줄에 가장 큰 덩어리가 차지하는 칸의 개수를 출력한다. 단, 덩어리가 없으면 0을 출력한다.</p>
56+
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import Foundation
2+
3+
let nq = readLine()!.split { $0 == " " }.map { Int(String($0))! }
4+
var n = Int(pow(2.0, Double(nq[0])))
5+
let q = nq[1]
6+
7+
var map = [[Int]]()
8+
for _ in 0..<n {
9+
let line = readLine()!.split { $0 == " " }.map { Int(String($0))! }
10+
map.append(line)
11+
}
12+
13+
let ls = readLine()!.split { $0 == " " }.map { Int(String($0))! }
14+
15+
for l in ls {
16+
// 1. 2^L x 2^L 크기의 부분 격자들을 시계 방향으로 90도 회전
17+
if l > 0 {
18+
let rotateSize = Int(pow(2.0, Double(l)))
19+
map = rotateMatrix(matrix: map, rotateSize: rotateSize)
20+
}
21+
22+
// 2. 얼음이 녹는 과정 (동시성 고려)
23+
map = meltIce(matrix: map)
24+
}
25+
26+
// 3. 최종 결과 계산
27+
let totalIce = sumAllElements(in: map)
28+
let largestMass = findLargestMass(in: map)
29+
30+
print(totalIce)
31+
print(largestMass)
32+
33+
34+
func rotateMatrix(matrix: [[Int]], rotateSize: Int) -> [[Int]] {
35+
let size = matrix.count
36+
var newMatrix = matrix // 회전 결과를 담을 새로운 배열
37+
38+
// 쿼드 트리 분할 및 회전
39+
for r in stride(from: 0, to: size, by: rotateSize) {
40+
for c in stride(from: 0, to: size, by: rotateSize) {
41+
rotateSubMatrixInPlace(&newMatrix, startX: c, startY: r, size: rotateSize)
42+
}
43+
}
44+
return newMatrix
45+
}
46+
47+
func rotateSubMatrixInPlace<T>(_ matrix: inout [[T]], startX: Int, startY: Int, size: Int) {
48+
// 1. 전치 (Transpose)
49+
for row in 0..<size {
50+
for col in row..<size {
51+
let temp = matrix[startY + row][startX + col]
52+
matrix[startY + row][startX + col] = matrix[startY + col][startX + row]
53+
matrix[startY + col][startX + row] = temp
54+
}
55+
}
56+
// 2. 각 행을 뒤집기 (Column Reversal)
57+
for row in 0..<size {
58+
for col in 0..<size / 2 {
59+
let temp = matrix[startY + row][startX + col]
60+
matrix[startY + row][startX + col] = matrix[startY + row][startX + size - 1 - col]
61+
matrix[startY + row][startX + size - 1 - col] = temp
62+
}
63+
}
64+
}
65+
66+
func meltIce(matrix: [[Int]]) -> [[Int]] {
67+
let rows = matrix.count
68+
let cols = matrix[0].count
69+
var newMatrix = matrix // 녹은 결과를 담을 새로운 배열
70+
71+
let directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
72+
73+
for r in 0..<rows {
74+
for c in 0..<cols {
75+
// 얼음이 있는 칸만 확인
76+
guard matrix[r][c] > 0 else { continue }
77+
var adjacentCount = 0
78+
79+
for (dr, dc) in directions {
80+
let neighborR = r + dr
81+
let neighborC = c + dc
82+
83+
// 인접 칸이 유효 범위 내에 있고, 얼음이 있다면 카운트
84+
if neighborR >= 0 && neighborR < rows && neighborC >= 0 && neighborC < cols && matrix[neighborR][neighborC] > 0 {
85+
adjacentCount += 1
86+
}
87+
}
88+
89+
// 인접한 얼음 칸이 3개 미만이면 녹임
90+
if adjacentCount < 3 {
91+
newMatrix[r][c] -= 1
92+
}
93+
}
94+
}
95+
return newMatrix
96+
}
97+
98+
func sumAllElements(in matrix: [[Int]]) -> Int {
99+
return matrix.reduce(0) { total, row in
100+
total + row.reduce(0, +)
101+
}
102+
}
103+
104+
func findLargestMass(in matrix: [[Int]]) -> Int {
105+
guard !matrix.isEmpty else { return 0 }
106+
let rows = matrix.count
107+
let cols = matrix[0].count
108+
var visited = Array(repeating: Array(repeating: false, count: cols), count: rows)
109+
var maxMass = 0
110+
let directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
111+
112+
for r in 0..<rows {
113+
for c in 0..<cols {
114+
if matrix[r][c] > 0 && !visited[r][c] {
115+
var currentMass = 0
116+
var queue = [(row: Int, col: Int)]()
117+
118+
queue.append((r, c))
119+
visited[r][c] = true
120+
121+
var queueIndex = 0
122+
while queueIndex < queue.count {
123+
let current = queue[queueIndex]
124+
queueIndex += 1
125+
126+
let currentRow = current.row
127+
let currentCol = current.col
128+
currentMass += 1 // 덩어리 크기를 1씩 증가
129+
130+
for (dr, dc) in directions {
131+
let nextRow = currentRow + dr
132+
let nextCol = currentCol + dc
133+
134+
if nextRow >= 0 && nextRow < rows && nextCol >= 0 && nextCol < cols &&
135+
!visited[nextRow][nextCol] && matrix[nextRow][nextCol] > 0 {
136+
137+
visited[nextRow][nextCol] = true
138+
queue.append((row: nextRow, col: nextCol))
139+
}
140+
}
141+
}
142+
maxMass = max(maxMass, currentMass)
143+
}
144+
}
145+
}
146+
return maxMass
147+
}

0 commit comments

Comments
 (0)