...
if !visited[new탐색할y좌표][new탐색할x좌표]
{
visited[new탐색할y좌표][new탐색할x좌표] = visited[현재 탐색중인 y좌표][현재 탐색중이x좌표] + 1
...
}
17141 : 연구소2 / 문제 소개
연구소(n by n )에 벽 1, 바이러스 놓을 수 있는 칸 2, 바이러스 퍼질 수 있는 칸 0이 있다.
M은 바이러스의 개수이다.
연구소 안 특정 바이러스 놓을 수 있는 칸(2)에 바이러스를 놓을 때 가장 최소한의 시간에
전부 바이러스를 퍼지게 하는 최소 시간을 출력하는 문제이다.
이 문제를 풀기 위해선
바이러스가 퍼질 때 bfs를 사용해야 하고,
그 이전에 바이러스를 놓을 수 있는 칸들 중에 중복되지 않는 칸들을 선택(조합 : combination)해서 바이러스가 퍼질 수 있는 시간을 전부 구하고 그 중 벽을 제외한 모든 칸에 퍼지는 바이러스가 최소 시간인 것을 찾으면 답이다.
풀이 과정
우선 맵을 받아올 때 바이러스를 놓을 수 있는 칸들의 좌표을 List배열 에 저장했다.
typealias point = (x:Int,y:Int,time:Int)
map = Array(repeating: [Int](), count: n)
var list = [point]()
for y in 0..<n
{
map[y] = readLine()!.split(separator:" ").map{Int(String($0))!}
for x in 0..<n
{
if map[y][x] == 2
{
list.append((x,y,0))
}
}
}
이 list의 index를 문제에서 주어진 놓을 수 있는 바이러스의 개수 M개를 선택해서 그 좌표들에 대해 bfs탐색을 하면된다.
여기서 중요한 것이 list.count == 5이고 M == 3 이라면
선택할 수 있는 원소들은 index로 표현할 수 있다.
0,1,2,3,4 중 3개..
이게 이 문제의 뽀인트인것 같다.(아닌가?)
0,1,2 | 0,1,3 | 0,1,4
1,2,3 | 1,2,4 | 1,2,5
1,3,4 | 1,3,5
2,3,4
이렇게 구하려면 재귀를 사용해야 한다.
let list = [0,1,2,3,4]
var selected = Array(repeating:false,count:list.count)
var dfs(_ index: Int, _ curCount: Int, _ needCount : Int)
{
if curCount == needCount
{
var result = ""
for i in 0..<selected.count
{
if selected[i]
{
//여기서 i는 선택된 index이다.
result += "\(i) "
}
}
print(result)
return
}
for i in index..<list.count
{
if selected[i] { continue }
selected[i] = true
dfs(i+1,curCount+1,needCount)
selected[i] = false
}
}
dfs(0,0,3)
재귀는 이런 형식으로 구현할 수 있다.
여기서 selected가 중요하다. 이것을 통해 특정 index들이 중복해서 선택되지 않게 해준다.
이렇게 index 조합을 구성하면 바이러스를 퍼트리고 최소시간을 체크 후 이전에 구한 시간과 비교하면 된다.
근데 아무리 선택해도 바이러스가 퍼지지 않는 부분이 있어서 이 부분도 체크를 해야한다.
참고,,
추가로 bfs탐색시에 시간 체크를 어떻게 하는지 간략하게 설명하겠다,,
queue의 타입은 point이다.(위에..정의했어요)
point의 x,y는 탐색할 좌표이고, time은 bfs탐색할 때 동시에 탐색을 이어나가기 위한 체크인 겸?? 최소시간을 체크한다.
왼쪽은 초기 큐 오른쪽은 bfs탐색을 1회 했을 때의 큐이다.
이걸 visited[[Int]] 2차원 배열에 저장해 나가도 되지만,
...
if !visited[new탐색할y좌표][new탐색할x좌표]
{
visited[new탐색할y좌표][new탐색할x좌표] = visited[현재 탐색중인 y좌표][현재 탐색중이x좌표] + 1
...
}
queue의 x,y 좌표뒤에 저장해 나가도 된다.
그 코드는 아래에 있다!!
코드 구현
import Foundation
//탐색할 x,y좌표 그때의 진행 상황
typealias point = (x:Int,y:Int,time:Int)
//탐색할 방향
let direction = [(-1,0),(1,0),(0,1),(0,-1)]
//최소시간 저장
var result = 2501
/**
TODO : 맵의 정보를 저장하는 클래스
- Param n : 맵 크기
- Param totalCnt : 선택해야 할 바이러스 M개 개수
- Param map : 맵 정보
- Param list : 맵에서 바이러스를 놓을 수 있는 좌표들 저장
*/
class mapInfo
{
let n : Int
let totalCnt : Int
var map : [[Int]]
var list : [point]
init()
{
let input = readLine()!.split(separator:" ").map{Int(String($0))!}
n = input[0]
totalCnt = input[1]
map = Array(repeating: [Int](), count: n)
list = [point]()
for y in 0..<n
{
map[y] = readLine()!.split(separator:" ").map{Int(String($0))!}
for x in 0..<n
{
if map[y][x] == 2
{
list.append((x,y,0))
}
}
}
}
}
/**
dfs를 통해 특정 index를 선택했다면 queue에 삽입될 것이고 해당 큐를 통해 바이러스가 퍼짐
- parameter m: 맵 정보.
- parameter visited: (중복 방문 안되기 위해) 방문 체크.
- parameter queue: 선택된 바이러스 좌표들.
# Notes: #
1. 만약 바이러스가 선택됬음에도 바이러스가 퍼지지 않는 칸이 있을 수 있다.
그래서 마지막에 완전 탐색으로 파악해준다.
*/
func bfs(_ m : mapInfo, _ visited : inout [[Bool]], _ queue : inout [point])
{
var index = 0
var time = 0
while queue.count != index
{
let (curX,curY,prevTime) = queue[index]
index += 1
if time == prevTime
{
time += 1
}
for (dx,dy) in direction
{
let (nx,ny) = (curX+dx,curY+dy)
if nx<0||nx>m.n-1||ny<0||ny>m.n-1||visited[ny][nx] { continue }
if m.map[ny][nx] != 1
{
visited[ny][nx] = true
queue.append((nx,ny,prevTime+1))
}
}
}
if time > result
{
return
}
for y in 0..<m.n
{
for x in 0..<m.n
{
if !visited[y][x] && m.map[y][x] != 1
{
return
}
}
}
result = time
}
/**
바이러스들이 있는 list에서 M개의 바이러스를 선택하기 위한 함수
- parameter index: 선택될 숫자의 시작.
- parameter curCnt: dfs함수 실행될 때 현재 몇개의 index들을 선택했는지?
- parameter selected: 선택된 숫자들을 체크하는 배열.
- parameter map: 맵 정보. 여기에 바이러스가 놓일 좌표들이 있는 list가 있음
# Notes: #
1. if curCnt == m.totalCnt
만약 바이러스 놓을 수 있는 특정 개수를 선택했다면
queue에 그 바이러스들 추가시키고 bfs탐색하기!!
*/
func dfs(_ index : Int,_ curCnt : Int,_ selected : inout [Bool],_ m : mapInfo )
{
if curCnt == m.totalCnt
{
var queue = [point]()
var visited = Array(repeating:Array(repeating:false,count:m.n),count:m.n)
for i in 0..<m.list.count
{
if selected[i]
{
let (x,y,time) = m.list[i]
visited[y][x] = true
queue.append((x,y,time))
}
}
bfs(m, &visited, &queue)
return
}
for i in index..<m.list.count
{
if selected[i] { continue }
selected[i] = true
dfs(i+1,curCnt+1, &selected, m)
selected[i] = false
}
}
// m = 맵 정보
// dfs호출 시 시작은 index 0 부터!! 이때 현재 선택된 개수도 0부터
func BOJ_17141()
{
let m = mapInfo()
var selected = Array(repeating:false,count:m.list.count)
dfs(0,0, &selected, m)
print(result == 2501 ? -1 : result - 1)
}
BOJ_17141()
visit my github
'백준 PS일지 > DFS&BFS' 카테고리의 다른 글
[백준/Swift] 2933: 미네랄 (0) | 2023.01.02 |
---|---|
[백준/Swift] 12852 : 1로 만들기 2 (0) | 2022.08.08 |
[백준/Swift] 2638 : 치즈. 거북이 같은 내 코드 개선시키기... (0) | 2022.07.21 |
[백준/Swift] 1726 : 로봇 (0) | 2022.07.18 |