https://www.acmicpc.net/problem/16920

 

16920번: 확장 게임

구사과와 친구들이 확장 게임을 하려고 한다. 이 게임은 크기가 N×M인 격자판 위에서 진행되며, 각 칸은 비어있거나 막혀있다. 각 플레이어는 하나 이상의 성을 가지고 있고, 이 성도 격자판 위

www.acmicpc.net

탐색 종료 조건 부분에서 고생했던 문제였다. 처음에는 지도를 입력받을 때 빈칸의 개수를 세어놓고 빈칸이 0이 될 때까지 플레이어 순서대로 경로탐색을 수행하는 방법으로 시도했으나, 벽으로 둘러싸인 빈 공간의 가능성을 뒤늦게 깨달았다. 

 

풀이

각 플레이어에게 해당하는 경로탐색용 큐 Array(repeating: [[Int]](), count: p+1)를 생성하여 앞으로 방문해야할 공간의 좌표를 담아둔다. 이후 모든 플레이어의 큐가 비어있는 상태가 될 때까지 각 플레이어 턴마다 Si번 경로탐색을 수행하면 된다. 큐의 첫 수행 좌표는 지도를 입력받을 때 큐에 담아두면 된다. 

 

정답 코드

'Problem Solving > BOJ' 카테고리의 다른 글

[1388] 바닥 장식  (0) 2022.10.31
[1063] 킹  (1) 2022.10.28
[9328] 열쇠  (0) 2022.10.25
[2011] 암호코드  (0) 2022.10.24
[11967] 불켜기  (0) 2022.10.19

https://www.acmicpc.net/problem/9328

 

9328번: 열쇠

상근이는 1층 빌딩에 침입해 매우 중요한 문서를 훔쳐오려고 한다. 상근이가 가지고 있는 평면도에는 문서의 위치가 모두 나타나 있다. 빌딩의 문은 모두 잠겨있기 때문에, 문을 열려면 열쇠가

www.acmicpc.net

오답 판정으로 고생했던 문제. 다른 분의 풀이를 보니 접근 불가능한 문을 마주했을 때와 이후 열쇠를 얻어 방문할 수 있게 되는 부분에서의 처리방법이 달랐다. 

 

풀이

bfs를 수행할 때 크게 4가지 조건을 확인하면서 경로를 탐색해야 한다.

  • 빈 공간을 마주한 경우
  • 문서를 마주한 경우
  • 문을 마주한 경우
  • 열쇠를 마주한 경우

문서와 빈 공간의 경우 알맞게 처리하고 경로탐색을 수행하면 된다.

헤맸던 부분은 열쇠를 마주했던 부분이다. 문과 열쇠 각각 두 가지 경우로 다시 나뉜다.

  • 열 수 있는 문을 마주한 경우 -> 그대로 경로 탐색 수행
  • 열 수 없는 문을 마주한 경우 -> 해당 좌표를 담아두고 다음 경로 탐색
  • 이미 얻었던 열쇠를 마주한 경우 -> 그대로 경로 탐색 수행
  • 기존에 없던 열쇠를 마주한 경우 -> 기존에 마주했던 열 수 없었던 문들 중 해당 열쇠로 열 수 있는 문 좌표에 대해 경로 탐색 수행

기존의 코드는 맵을 입력받을 때 모든 문의 좌표를 담아두고 경로탐색 시 새로운 열쇠를 얻을 때마다 좌표를 담아두었던 배열을 돌며 탐색을 수행했었다. 문제는 그렇게 되면 벽으로 둘러싸여 도달할 수 없는 문의 경우를 매번 마주해야 하고, 이러한 예외처리를 해주어야 하는데, 거기서 부터 해결을 못해 오답 판정을 받았다.

 

그리고 또 한번 곤욕을 겪은 부분이 경로 탐색 시작 지점에 대한 부분이다. 처음엔 문제에서 주어진 대로 맵의 테두리만 돌면서 탐색 가능한 지점을 직접 찾는 코드를 적었다. 여기서부터 시작 가능한 지점에 대해서 다시 한번 위의 4가지 경우에 맞게 예외처리를 해주어야 했고 이렇게 되면서 코드 양도 많아지고 오답 판정을 받았을 때 찾아봐야 할 지점이 너무 많아서 해결을 못했다.

 

다른 분의 풀이에서 이 부분도 잘 해결한 모습을 찾을 수 있다. 입력받은 맵 주변에 빈 공간으로 한번 둘러주어 map[0][0]에서 경로를 탐색하게 되면 자연스럽게 4가지 조건에 대한 처리가 이루어지게 된다.

//입력된 지도				
*	*	*	*	*	*	*	*	*	*	*	*	*	*	*	*	*
.	.	.	.	.	.	.	.	.	.	.	.	.	*	*	$	*
*	B	*	A	*	P	*	C	*	*	X	*	Y	*	.	X	.
*	y	*	x	*	a	*	p	*	*	$	*	$	*	*	$	* 
*	*	*	*	*	*	*	*	*	*	*	*	*	*	*	*	*
						
//테두리 추가
.	.	.	.	.	.	.	.	.	.	.	.	.	.	.	.	.	.	.
.	*	*	*	*	*	*	*	*	*	*	*	*	*	*	*	*	*	.
.	.	.	.	.	.	.	.	.	.	.	.	.	.	*	*	$	*	.
.	*	B	*	A	*	P	*	C	*	*	X	*	Y	*	.	X	.	.
.	*	y	*	x	*	a	*	p	*	*	$	*	$	*	*	$	*	.
.	*	*	*	*	*	*	*	*	*	*	*	*	*	*	*	*	*	.
.	.	.	.	.	.	.	.	.	.	.	.	.	.	.	.	.	.	.

 

정답 코드

'Problem Solving > BOJ' 카테고리의 다른 글

[1063] 킹  (1) 2022.10.28
[16920] 확장게임  (0) 2022.10.26
[2011] 암호코드  (0) 2022.10.24
[11967] 불켜기  (0) 2022.10.19
[2146] 다리 만들기  (2) 2022.10.13

https://www.acmicpc.net/problem/2011

 

2011번: 암호코드

나올 수 있는 해석의 가짓수를 구하시오. 정답이 매우 클 수 있으므로, 1000000으로 나눈 나머지를 출력한다. 암호가 잘못되어 암호를 해석할 수 없는 경우에는 0을 출력한다.

www.acmicpc.net

답이 생각나지 않아 점화식을 찾아보고 코드를 적어봤는데, 풀이를 이해하고서도 예외처리를 제대로 하지 못해서 오답 판정을 받았다. 결국 코드까지 찾아보고 난 후에야 풀 수 있었던 문제. 역시나 DP문제는 다양한 시점으로 생각하는 연습이 필요한 것 같다.

 

풀이

암호화된 문자의 길이가 n일 경우, dp:[Int] 배열은 총 n개의 배열로 dp[i]는 0번째 숫자부터 i번째 숫자까지로 만들수 있는 암호의 갯수가 담긴 배열이다. 입력된 암호문구를 순회하면서 현재 숫자가 0보다 크다면 dp[i-1]번째 경우의 수를 이어가면 되고, 추가로 직전 숫자가 0보다 크다면 현재 숫자와 합쳐 두 자리수의 대체 가능한 알파벳을 만들수 있다면 dp[i-2]번째 경우의 수를 추가로 이어가면된다. 즉 점화식은 아래와 같다.

현재 암호가 0보다 크다면,
dp[i] += dp[i-1]
추가로 직전 암호가 0보다 커서 현재 암호와 이어 붙였을 시 10 이상 26 이하라면,
dp[i] += dp[i-2]

점화식을 알았을 때는 간단하게 풀면 되겠다 싶었는데, 문제는 중간에 나오는 0에 대한 처리 부분이었다. 처음에는 중간에 flag 변수를 만들어 분기 처리를 하였는데, 생각보다 처리해야 할 경우가 많아서 오답처리를 받았다. 다른 유형의 문제들은 많은 문제를 풀면서 패턴에 적응해가는 과정을 통해 성장해가는 게 느껴지는 반면, DP문제는 다양한 시각에서 문제를 바라봐야 한다는 점이 여러모로 어렵게 느껴진다. 풀이는 해당 블로그에서 찾았다. 이해하기 쉽게 풀이에 대한 이미지도 있다.

 

정답 코드

'Problem Solving > BOJ' 카테고리의 다른 글

[16920] 확장게임  (0) 2022.10.26
[9328] 열쇠  (0) 2022.10.25
[11967] 불켜기  (0) 2022.10.19
[2146] 다리 만들기  (2) 2022.10.13
[17071] 숨바꼭질 5  (0) 2022.10.12

https://www.acmicpc.net/problem/11967

 

11967번: 불켜기

(1, 1)방에 있는 스위치로 (1, 2)방과 (1, 3)방의 불을 켤 수 있다. 그리고 (1, 3)으로 걸어가서 (2, 1)방의 불을 켤 수 있다. (2, 1)방에서는 다시 (2, 2)방의 불을 켤 수 있다. (2, 3)방은 어두워서 갈 수 없으

www.acmicpc.net

처음으로 4차원 배열을 사용하여 풀어본 문제다. 입력 범위가 작아서 메모리 초과는 면할 수 있었다고 생각한다.

 

풀이

헛간을 표현하는 배열 map은 각 좌표에 스위치로 불을 켤 수 있는 다른 방의 좌표를 담기 위해 map[n][n] = [[a,b]...] 형태의 4차원 배열로 사용한다. 불이 켜져 있는지 확인하는 turnOn[n][n]:[[Bool]]() 배열과 방문 여부를 담는 visited[n][n] = [[Bool]]() 배열을 사용하여 좌표 탐색을 진행하면 된다.

 

한 가지 특이점은 예제를 보면 불을 켜게 되는 좌표가 현재 방문한 위치와 인접한 곳에 있지 않는 경우이다.([1][3]에 방문했을 때) 따라서 불을 켜게 될 때 불이 켜지게 되는 좌표의 인접 배열을 탐색하여 인접 배열중 방문했던 곳이 있는지 확인하여 기존에 방문했던 좌표에 인접한 곳이라면 다음 좌표 탐색의 큐에 넣으면 된다. 이후 기존과 동일하게 현재 위치를 기점으로 인접 배열을 확인하여 불이 켜져 있고, 방문한 적 없는 좌표를 큐에 추가로 담아 경로탐색을 수행하면 된다.

 

정답 코드

import Foundation

let nm = readLine()!.split(separator: " ").map{Int(String($0))!}
let n = nm[0]
let m = nm[1]
var map = Array(repeating: Array(repeating: [[Int]](), count: n), count: n)
var turnOn = Array(repeating: Array(repeating: false, count: n), count: n)
var visited = Array(repeating: Array(repeating: false, count: n), count: n)
var ans = 0

for _ in 0..<m{
    let input = readLine()!.split(separator: " ").map{Int(String($0))! - 1}
    let x = input[0]
    let y = input[1]
    let a = input[2]
    let b = input[3]
    map[x][y].append([a,b])
}

let dx = [-1,1,0,0]
let dy = [0,0,-1,1]
func bfs(){
    var q = [[0,0]]
    var dq = [[Int]]()
    while !q.isEmpty{
        dq = q.reversed()
        q.removeAll()
        for _ in 0..<dq.count{
            let curr = dq.removeLast()
            let x = curr[0]
            let y = curr[1]
            for button in map[x][y]{
                let ax = button[0]
                let by = button[1]
                if !turnOn[ax][by] {
                    turnOn[ax][by] = true
                    ans += 1
                    for i in 0..<4{
                        let nx = ax+dx[i]
                        let ny = by+dy[i]
                        if nx<0 || nx>=n || ny<0 || ny>=n{ continue }
                        if visited[nx][ny]{
                            visited[ax][by] = true
                            q.append([ax,by])
                        }
                    }
                }
            }
            for i in 0..<4{
                let nx = x+dx[i]
                let ny = y+dy[i]
                if nx<0 || nx>=n || ny<0 || ny>=n{ continue }
                if turnOn[nx][ny] && !visited[nx][ny]{
                    visited[nx][ny] = true
                    q.append([nx,ny])
                }
            }
        }
    }
}

turnOn[0][0] = true
visited[0][0] = true
ans = 1
bfs()
print(ans)

'Problem Solving > BOJ' 카테고리의 다른 글

[9328] 열쇠  (0) 2022.10.25
[2011] 암호코드  (0) 2022.10.24
[2146] 다리 만들기  (2) 2022.10.13
[17071] 숨바꼭질 5  (0) 2022.10.12
[2206] 벽 부수고 이동하기  (0) 2022.10.06

MKMapView를 사용하는 프로젝트를 다루던 중 시뮬레이터로 빌드 시 지도 부분이 그리드 화면으로 표시되는 문제가 발생했다. 순간 코드가 잘못되었나 생각이 들었지만, 실기기로 빌드해보면 정상 동작했다. 해당 문제는 시뮬레이터에서 발생한 문제로 개인적인 추측으로는 지도를 불러오는 데에 문제가 생긴 모양이다. 여러 가지 설정을 찾아보니 기기를 재설정하는 메뉴가 있었고 해당 메뉴로 해결할 수 있었다.

시뮬레이터 메뉴에 Device - Erase All Content and Settings를 누르면 나오는 확인창에 Erase를 선택하면 시뮬레이터가 재설정되고 정상적으로 지도를 불러오는 모습을 확인할 수 있다.

https://www.acmicpc.net/problem/2146

 

2146번: 다리 만들기

여러 섬으로 이루어진 나라가 있다. 이 나라의 대통령은 섬을 잇는 다리를 만들겠다는 공약으로 인기몰이를 해 당선될 수 있었다. 하지만 막상 대통령에 취임하자, 다리를 놓는다는 것이 아깝다

www.acmicpc.net

bfs를 두 번 사용해서 풀었다. 중간에 변수명이 겹친걸 못 알아채서 쓸데없는 부분에서 시간을 오래 끌었던 문제였다. 조금 더 집중해서 풀이를 생각하는 연습이 필요한 것 같다.

 

풀이

입력으로 육지와 바다에 대한 구분이 들어온다. 우선 기본적인 bfs를 통해 각 컴포넌트를 구분 짓는 작업을 해준다. 그 이후 한번 더 bfs를 통해 짧은 다리를 찾으면 되는데, 경로탐색의 조건은 다음과 같이 정의했다.

  • 상하좌우를 확인하여, 바다인 경우만 큐에 넣고 탐색
  • 바다가 아닌 곳이라면 경로탐색 시작 컴포넌트와 다른 컴포넌트인지 확인하여 다른 곳이라면 다리 길이를 갱신 후 종료
  • 경로 탐색 중 현재 갱신된 다리 길이보다 길다면 종료

 

정답 코드

주석 처리된 부분은 알맞게 풀고 있는지 확인하려 적은 코드이다.

import Foundation

let n = Int(readLine()!)!
var map = Array(repeating: [Int](), count: n)
var visited = Array(repeating: Array(repeating: false, count: n), count: n)
var land = Array(repeating: Array(repeating: 0, count: n), count: n)

for i in 0..<n{
    let input = readLine()!.split(separator: " ").map{Int(String($0))!}
    map[i] = input
}

let dx = [-1,1,0,0]
let dy = [0,0,-1,1]
func numbering(x:Int, y:Int, landNumber:Int){
    var q = [[x,y]]
    var dq = [[Int]]()
    while !q.isEmpty{
        dq = q.reversed()
        q.removeAll()
        for _ in 0..<dq.count{
            let curr = dq.removeLast()
            let x = curr[0]
            let y = curr[1]
            for i in 0..<4{
                let nx = x+dx[i]
                let ny = y+dy[i]
                if nx<0 || nx>=n || ny<0 || ny>=n || map[nx][ny]==0{ continue }
                if !visited[nx][ny]{
                    visited[nx][ny] = true
                    land[nx][ny] = landNumber
                    q.append([nx,ny])
                }
            }
        }
    }
}

var number = 1
for i in 0..<n{
    for k in 0..<n{
        if !visited[i][k] && map[i][k] != 0{
            visited[i][k] = true
            land[i][k] = number
            numbering(x: i, y: k, landNumber: number)
            number += 1
        }
    }
}
//for l in land{ print(l) }
var ans = n*n
var check = visited
func bridge(ix:Int,ky:Int){
    visited = Array(repeating: Array(repeating: false, count: n), count: n)
    var q = [[ix,ky,0]]
    var dq = [[Int]]()
    let landNumber = land[ix][ky]
    while !q.isEmpty{
        dq = q.reversed()
        q.removeAll()
        for _ in 0..<dq.count{
            let curr = dq.removeLast()
            let x = curr[0]
            let y = curr[1]
            let cnt = curr[2]
            if cnt > ans{
                return
            }
            for i in 0..<4{
                let nx = x+dx[i]
                let ny = y+dy[i]
                if nx<0 || nx>=n || ny<0 || ny>=n { continue }
                if !visited[nx][ny] && land[nx][ny]==0{
                    visited[nx][ny] = true
                    q.append([nx,ny,cnt+1])
                }
                if !visited[nx][ny] && land[nx][ny]>0 && land[nx][ny] != landNumber{
//                    print("land[\(ix)][\(ky)]:\(landNumber) -> land[\(nx)][\(ny)]:\(land[nx][ny]), cnt:\(cnt)")
                    ans = min(ans, cnt)
                    return
                }
            }
        }
    }
}

visited = Array(repeating: Array(repeating: false, count: n), count: n)
for x in 0..<n{
    for y in 0..<n{
        if land[x][y]>0{
            bridge(ix: x, ky: y)
        }
    }
}
print(ans)

'Problem Solving > BOJ' 카테고리의 다른 글

[2011] 암호코드  (0) 2022.10.24
[11967] 불켜기  (0) 2022.10.19
[17071] 숨바꼭질 5  (0) 2022.10.12
[2206] 벽 부수고 이동하기  (0) 2022.10.06
[1600] 말이 되고픈 원숭이  (2) 2022.10.05

https://www.acmicpc.net/problem/17071

 

17071번: 숨바꼭질 5

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 500,000)에 있고, 동생은 점 K(0 ≤ K ≤ 500,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일 때

www.acmicpc.net

숨바꼭질 시리즈의 문제다. 선행 문제와 같은 방식으로 풀어보려 했으나 이번에는 동생이 움직인다는 조건이 붙어서 오답을 받았다. 풀이를 찾아보고 풀 수 있었던 문제였다. 

 

풀이

기존의 숨바꼭질 문제에서는 visited[n] 방문 배열을 "거리 n에 도달하는 데에 걸리는 시간"으로 설정하여 경로탐색으로 더 짧은 시간으로 갱신시켜 답을 구했다면 이번에는 동생이 이동한다는 조건 때문에 다른 방식으로 접근해야 한다. 수빈이가 동생이 도착할 장소에 미리 도착하여 한 칸 전진, 한 칸 후진 즉 2초를 소비하여 해당 자리에서 대기하는 것이 가능하다.

 

그렇다면 구분해야할것은 해당 시간과 위치에 홀수 초에 도착하는지 짝수 초에 도착하는지를 확인하여 해당 시간을 반환하면 된다. 따라서 방문 배열은 visited[t][k]:[[Bool]] 2차원 배열로 설정하고 t%2초에 k번째 위치에 방문했는지를 갱신하여 경로탐색을 해주면 된다. 시간이 갱신될 때마다 동생의 위치 k번째 배열 값이 참이라면 해당 시간을 반환하면 된다. 역시 설명보단 코드가 이해하기 더 쉬울 것이다.

 

정답 코드

import Foundation

let nk = readLine()!.split(separator: " ").map{Int($0)!}
let n = nk[0]
var k = nk[1]
var ans = -1

var map = Array(repeating: Array(repeating: false, count: 2), count: 500001)
map[n][0] = true
var time = 0

func bfs(){
    var q = [n]
    var dq = [Int]()
    while !q.isEmpty{
        k += time
        dq = q.reversed()
        q.removeAll()
        if k<0 || k>500000{return}
        if map[k][time%2]{
            ans = time
            return
        }
        for _ in 0..<dq.count{
            let curr = dq.removeLast()
            let next = [curr-1,curr+1,curr*2]
            
            for next in next{
                if next<0 || next>=500001{continue}
                if !map[next][(time+1)%2]{
                    map[next][(time+1)%2] = true
                    q.append(next)
                }
            }
        }
        time += 1
    }
}
bfs()
print(ans)

 

 

'Problem Solving > BOJ' 카테고리의 다른 글

[11967] 불켜기  (0) 2022.10.19
[2146] 다리 만들기  (2) 2022.10.13
[2206] 벽 부수고 이동하기  (0) 2022.10.06
[1600] 말이 되고픈 원숭이  (2) 2022.10.05
[4179] 불!  (0) 2022.09.29

https://www.acmicpc.net/problem/2206

 

2206번: 벽 부수고 이동하기

N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로

www.acmicpc.net

골드 난이도의 BFS문제를 여러 개 풀어보면서 느끼는 공통점은 기본적으로 3차원 배열을 통해 문제를 풀어야 한다는 점이다. 

 

풀이

크게 3가지 케이스로 나누어 경로탐색을 진행해야 한다.

  • 벽을 아직 부수지 않은 상태에서 길을 만났을 경우
  • 벽을 아직 부수지 않은 상태에서 벽을 만났을 경우
  • 이미 벽을 부순상태에서 길을 만났을 경우

경로를 확인하는 visited 배열은 지난번 [1600] 말이 되고픈 원숭이 문제와 같다. 지난번에는 말의 움직임을 몇 번 사용했는지를 확인했다면, 이번에는 벽을 부쉈는지(1), 부수지 않았는지(0) 두 가지만 판별하면 되므로 visited[x][y][2]의 형태로 선언하면 된다.

 

정답 코드

import Foundation

let nm = readLine()!.split(separator: " ").map{Int($0)!}
let n = nm[0]
let m = nm[1]

var map = Array(repeating: [Int](), count: n)
var visited = Array(repeating: Array(repeating: Array(repeating: false, count: 2), count: m), count: n)
for i in 0..<n{
    map[i] = readLine()!.map{Int(String($0))!}
}

let dx = [-1,1,0,0]
let dy = [0,0,-1,1]
var ans = -1

func bfs(){
    var q = [[0,0,0,1]]
    var dq = [[Int]]()
    visited[0][0][0] = true
    while !q.isEmpty{
        dq = q.reversed()
        q.removeAll()
        for _ in 0..<dq.count{
            let curr = dq.removeLast()
            let x = curr[0]
            let y = curr[1]
            let wall = curr[2]
            let cnt = curr[3]
            
            if x==n-1 && y==m-1{
                ans = cnt
                return
            }
            
            for i in 0..<4{
                let nx = x + dx[i]
                let ny = y + dy[i]
                if nx<0 || nx>=n || ny<0 || ny>=m {continue}
                
                if wall==0 && map[nx][ny]==0 && !visited[nx][ny][0]{
                    visited[nx][ny][0] = true
                    q.append([nx,ny,0,cnt+1])
                }
                if wall==0 && map[nx][ny]==1 && !visited[nx][ny][1]{
                    visited[nx][ny][1] = true
                    q.append([nx,ny,1,cnt+1])
                }
                if wall==1 && map[nx][ny]==0 && !visited[nx][ny][1]{
                    visited[nx][ny][1] = true
                    q.append([nx,ny,wall,cnt+1])
                }
            }
        }
    }
}
bfs()
print(ans)

'Problem Solving > BOJ' 카테고리의 다른 글

[2146] 다리 만들기  (2) 2022.10.13
[17071] 숨바꼭질 5  (0) 2022.10.12
[1600] 말이 되고픈 원숭이  (2) 2022.10.05
[4179] 불!  (0) 2022.09.29
[2294] 동전 2  (0) 2022.09.22

+ Recent posts