문제

명우는 홍준이와 함께 팰린드롬 놀이를 해보려고 한다.

먼저, 홍준이는 자연수 N개를 칠판에 적는다. 그 다음, 명우에게 질문을 총 M번 한다.

각 질문은 두 정수 S와 E(1 ≤ S ≤ E ≤ N)로 나타낼 수 있으며, S번째 수부터 E번째 까지 수가 팰린드롬을 이루는지를 물어보며, 명우는 각 질문에 대해 팰린드롬이다 또는 아니다를 말해야 한다.

예를 들어, 홍준이가 칠판에 적은 수가 1, 2, 1, 3, 1, 2, 1라고 하자.

  • S = 1, E = 3인 경우 1, 2, 1은 팰린드롬이다.
  • S = 2, E = 5인 경우 2, 1, 3, 1은 팰린드롬이 아니다.
  • S = 3, E = 3인 경우 1은 팰린드롬이다.
  • S = 5, E = 7인 경우 1, 2, 1은 팰린드롬이다.

자연수 N개와 질문 M개가 모두 주어졌을 때, 명우의 대답을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 수열의 크기 N (1 ≤ N ≤ 2,000)이 주어진다.

둘째 줄에는 홍준이가 칠판에 적은 수 N개가 순서대로 주어진다. 칠판에 적은 수는 100,000보다 작거나 같은 자연수이다.

셋째 줄에는 홍준이가 한 질문의 개수 M (1 ≤ M ≤ 1,000,000)이 주어진다.

넷째 줄부터 M개의 줄에는 홍준이가 명우에게 한 질문 S와 E가 한 줄에 하나씩 주어진다.

출력

총 M개의 줄에 걸쳐 홍준이의 질문에 대한 명우의 답을 입력으로 주어진 순서에 따라서 출력한다. 팰린드롬인 경우에는 1, 아닌 경우에는 0을 출력한다.

내가 푼 풀이

- 처음에는 주어진 질문에대한 모든 경우의 수에서 팰린드롬인지 구하려고 했지만, 수열의 크기를 감안했을 때 시간초과가 날 거라 생각했다.

- dp를 이용하여 dp 배열에 저장하고, 질문들을 해당 배열에서 꺼내오려한다.

- dp에 저장하기 위해 팰린드롬의 성질을 좀 더 생각해봐야 했다.

- 여기서 이용했던 팰린드롬의 성질은 주어진 수열에서 s번째부터 e까지의 수열이 팰린드롬이라면, s+1번째부터 e-1까지의 수열도 역시 팰린드롬이 된다.

- 예로 위에 주어진 수열 1, 2, 1, 3, 1, 2, 1에서 0번째부터 6번째까지의 수열이 1, 2, 1, 3, 1, 2, 1이고, 해당 수열은 팰린드롬이다.

- 0번째부터 6번째까지의 수열이 팰린드롬이라면, 1번째부터 5번째까지의 수열도 역시 팰린드롬이다. ( 2, 1, 3, 1, 2 )

- 위의 성질을 이용하기 위해선 주어진 수열의 길이가 최소 3이어야 한다.

- 수열의 길이가 1이라면 원소 자기 자신하나로 팰린드롬이 된다.

- 수열의 길이가 2라면 원소 하나와 그다음 원소가 같다면, 팰린드롬이 된다.

- 수열의 길이가 3 미만인 경우의 수는 위와 같이 구하고, 3 이상인 경우는 아래와 같이 구한다.

- dp [i][j] = i번째부터 j번째까지의 수열이 팰린드롬인가? ( 팰린드롬이라면 1을 저장, 아니라면 0을 저장)

- 위 성질을 이용하여 수열의 길이가 3이상이라면, 두가지 경우의 수를 판단하면된다.

1. 수열의 양 끝 원소가 같은지?

2. 주어진 수열의 s+1번째부터 e-1까지의 수열이 팰린드룸인지?

 

해당 문제는 많은 입력과 출력을 요구하므로, readLine을 이용한 입력및 print 출력을 남용하면 시간초과가 뜬다.

더 빠른 FileIO를 이용하여 입력 및 한 문자열에 저장하여 한번에 출력하는 방식을 이용한다.

 

코드로 구현해보자.

import Foundation

// Swift 입출력
final class FileIO {
    private var buffer:[UInt8]
    private var index: Int
    
    init(fileHandle: FileHandle = FileHandle.standardInput) {
        buffer = Array(fileHandle.readDataToEndOfFile())+[UInt8(0)]
        index = 0
    }
    
    @inline(__always) private func read() -> UInt8 {
        defer { index += 1 }
        
        return buffer.withUnsafeBufferPointer { $0[index] }
    }
    
    @inline(__always) func readInt() -> Int {
        var sum = 0
        var now = read()
        var isPositive = true
        
        while now == 10
                || now == 32 { now = read() }
        if now == 45{ isPositive.toggle(); now = read() }
        while now >= 48, now <= 57 {
            sum = sum * 10 + Int(now-48)
            now = read()
        }
        
        return sum * (isPositive ? 1:-1)
    }
    
    @inline(__always) func readString() -> String {
        var str = ""
        var now = read()
        
        while now == 10
                || now == 32 { now = read() }
        
        while now != 10
                && now != 32 && now != 0 {
            str += String(bytes: [now], encoding: .ascii)!
            now = read()
        }
        
        return str
    }
}

// 입력
let fileIO = FileIO()
let N = fileIO.readInt()
var arr = [Int]()
for i in 0..<N {
    arr.append(fileIO.readInt())
}
let M = fileIO.readInt()
var dp = Array(repeating: Array(repeating: 0, count: N), count: N)

// 수열의 길이가 1일때
for i in 0..<N {
    dp[i][i] = 1
}

// 수열의 길이가 2일때
for i in 0..<N-1 {
    if arr[i] == arr[i+1] {
        dp[i][i+1] = 1
    }
}

// 수열의 길이가 3이상일때
for i in (0..<N-2).reversed() {
    for j in (i+2..<N).reversed() {
        let idx = j
        if arr[i] == arr[idx] && dp[i+1][idx-1] == 1 {
            dp[i][idx] = 1
        }
    }
}
var answer = ""
for i in 0..<M {
    let s1 = fileIO.readInt()
    let s2 = fileIO.readInt()
    answer += "\(dp[s1-1][s2-1])\n"
}

print(answer)

문제

N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로로 이동하려 한다. 최단경로는 맵에서 가장 적은 개수의 칸을 지나는 경로를 말하는데, 이때 시작하는 칸과 끝나는 칸도 포함해서 센다.

만약에 이동하는 도중에 한 개의 벽을 부수고 이동하는 것이 좀 더 경로가 짧아진다면, 벽을 한 개 까지 부수고 이동하여도 된다.

한 칸에서 이동할 수 있는 칸은 상하좌우로 인접한 칸이다.

맵이 주어졌을 때, 최단 경로를 구해 내는 프로그램을 작성하시오.

입력

첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 1,000)이 주어진다. 다음 N개의 줄에 M개의 숫자로 맵이 주어진다. (1, 1)과 (N, M)은 항상 0이라고 가정하자.

출력

첫째 줄에 최단 거리를 출력한다. 불가능할 때는 -1을 출력한다.

내가 푼 풀이

- 첫 시도로 백트래킹을 이용해 0,0 지점에서 N, M 지점까지 가는 경우의 수를 조사했지만, 시간초과가 떴다.

- 이전에 지나갔던 지점을 1로 변경하며 세 보았는데 이론적으로 틀린것 같다.

- 벽을 단 한번 부술 수 있지만, 벽을 아예 부수지 않아도 최단경로가 나올 수 있다.

- 이를 두가지 경우의 수로 최적의 경로를 탐색보다는 기록하는 방향으로 정했다.

- m 과 n은 최대 1000이므로 BFS로 딱 한번 탐색해내야 한다.

- 3차원 배열을 지정해 벽을 부순경우와 부수지 않은 경우 모두 기록하여 M, N 지점에 도달했을 때, 최단경로를 출력한다.

- M N 지점까지 이동하지 못한경우 -1 출력

- visited[z][x][y] : z 가 0인경우, 벽을 아직 부수지 않았다. , z가 1인경우 벽을 이미 한번 부쉈다.

 

 

import Foundation

// 입력
let input = readLine()!.split(separator: " ").map{ Int($0)! }
var arr = [[Int]]()
for i in 0..<input[0] {
    let a = Array(readLine()!).map{ Int(String($0))! }
    arr.append(a)
}

// 최단경로를 기록할 visited 배열과 BFS를 이용할 needVistedQueue 배열
var visited = Array(repeating: Array(repeating: Array(repeating: 0, count: input[1]), count: input[0]), count: 2)
var needVisitQueue = [(x: Int, y: Int, z: Int)]()

// 상하좌우 방향
let dx = [0,1,0,-1]
let dy = [1,0,-1,0]

// 결과 변수
var result = Int.max

// 출발지점 설정과 출발지점도 최단경로에 세므로 1로 초기화
needVisitQueue.append((x: 0, y: 0, z: 0))
visited[0][0][0] = 1


// BFS
var idx = 0
while idx < needVisitQueue.count {
    var loc = needVisitQueue[idx]
    idx += 1
    
    // 목표지점에 도착한 경우 최단경로 갱신
    if loc.x == input[0] - 1 && loc.y == input[1] - 1 {
        if result > visited[loc.z][loc.x][loc.y] && visited[loc.z][loc.x][loc.y] != 0 {
            result = visited[loc.z][loc.x][loc.y]
        }
        continue
    }
    for i in 0..<4 {
        let mx = dx[i] + loc.x
        let my = dy[i] + loc.y
        let mz = loc.z

        if mx >= 0 && mx < input[0] && my >= 0 && my < input[1] {
            // 다음 위치로 이동 할 수 있고, 아직 방문하지않은곳이라면
            if arr[mx][my] == 0 && visited[mz][mx][my] == 0 {
                visited[mz][mx][my] = visited[loc.z][loc.x][loc.y] + 1
                needVisitQueue.append((x: mx, y: my, z: mz))
            }
            // 다음 위치가 벽이고 아직 부수지 않았다면
            if arr[mx][my] == 1 && mz == 0 {
                visited[mz+1][mx][my] = visited[loc.z][loc.x][loc.y] + 1
                needVisitQueue.append((x: mx, y: my, z: mz + 1))
            }
        }
    }
}

if result == Int.max {
    print(-1)
} else {
    print(result)
}

문제

스네이크버드는 뱀과 새의 모습을 닮은 귀여운 생물체입니다. 

스네이크버드의 주요 먹이는 과일이며 과일 하나를 먹으면 길이가 1만큼 늘어납니다.

과일들은 지상으로부터 일정 높이를 두고 떨어져 있으며 i (1 ≤ i  N) 번째 과일의 높이는 hi입니다. 

스네이크버드는 자신의 길이보다 작거나 같은 높이에 있는 과일들을 먹을 수 있습니다.

스네이크버드의 처음 길이가 L일때 과일들을 먹어 늘릴 수 있는 최대 길이를 구하세요.

입력

첫 번째 줄에 과일의 개수 N (1 ≤ N ≤ 1,000) 과 스네이크버드의 초기 길이 정수 L (1 ≤ L ≤ 10,000) 이 주어집니다.

두 번째 줄에는 정수 h1, h2, ..., hN (1 ≤ hi ≤ 10,000) 이 주어집니다.

출력

첫 번째 줄에 스네이크버드의 최대 길이를 출력합니다.

내가 푼 풀이

- 스네이크버드는 자신의 길이보다 작거나 같은 높이의 과일을 먹을 수 있으므로 높이가 가장 낮은 과일부터 먹는다.

- 높이가 가장 낮은곳에 있는 과일부터 먹으며 과일을 못먹는경우 늘어난 길이 출력 , 다먹어도 출력

 

import Foundation

// 입력
let input = readLine()!.split(separator: " ").map{ Int($0)! }
var fruits = readLine()!.split(separator: " ").map{ Int($0)! }
var len = input[1]

// 과일의 높이가 담긴 배열 내림차순 정렬
fruits.sort{ $0 > $1 }

// 과일먹기
// 해당높이가 스네이크버드의 길이보다 작거나 같으면 먹고 길이가 자란다.
while !fruits.isEmpty {
    let food = fruits.removeLast()
    if len >= food {
        len += 1
    } else {
        break
    }
}
print(len)

문제

수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오.

예를 들어, 수열 A = {10, 20, 10, 30, 20, 50}인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이고, 길이는 4이다.

입력

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000)이 주어진다.

둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000)

출력

첫째 줄에 수열 A의 가장 긴 증가하는 부분 수열의 길이를 출력한다.

둘째 줄에는 가장 긴 증가하는 부분 수열을 출력한다. 그러한 수열이 여러가지인 경우 아무거나 출력한다.

내가 푼 풀이

- 이중 반복문으로 현재원소가 이전원소보다 크다면, dp에 저장된 배열 + 현재원소를 저장하여 가장 긴 증가하는 부분수열을 최신화한다.

- dp는 2차원배열로 각 인덱스는 해당 위치에서 가장 긴 부분수열을 갖는다.

 

import Foundation

// 입력
let input = Int(readLine()!)!
var arr = readLine()!.split(separator: " ").map{ Int($0)! }

// r: 결과배열, dp: 가장 긴 부분수열을 저장할 배열
var r = [Int]()
var dp = Array(repeating: [Int](), count: input)

// 수열의 각 원소는 최소 자신만 갖는 부분수열로 길이가 1이다.
// 현재원소가 이전원소보다 크다면 이전원소의 dp에 저장된 배열 + 현재원소를 저장하여 최신화
for i in 0..<arr.count {
    dp[i] = [arr[i]]
    for j in 0..<i {
        if arr[i] > arr[j] {
            var a = dp[j]
            a.append(arr[i])
            if a.count > dp[i].count {
                dp[i] = a
            }
        }
    }
    // 가장 긴 부분수열 최신화
    if dp[i].count > r.count {
        r = dp[i]
    }
}

print(r.count)
print(r.map{String($0)}.joined(separator: " "))

'코딩테스트 > 백준' 카테고리의 다른 글

BOJ-2206 벽 부수고 이동하기 Swift  (0) 2023.06.30
BOJ-16435 스네이크버드 Swift  (0) 2023.06.29
BOJ-1987 알파벳 Swift  (0) 2023.06.14
BOJ-15904 UCPC는 무엇의 약자일까? Swift  (0) 2023.06.13
BOJ-1890 점프 Swift  (0) 2023.06.13

문제

세로 R칸, 가로 C칸으로 된 표 모양의 보드가 있다. 보드의 각 칸에는 대문자 알파벳이 하나씩 적혀 있고, 좌측 상단 칸 (1행 1열) 에는 말이 놓여 있다.

말은 상하좌우로 인접한 네 칸 중의 한 칸으로 이동할 수 있는데, 새로 이동한 칸에 적혀 있는 알파벳은 지금까지 지나온 모든 칸에 적혀 있는 알파벳과는 달라야 한다. 즉, 같은 알파벳이 적힌 칸을 두 번 지날 수 없다.

좌측 상단에서 시작해서, 말이 최대한 몇 칸을 지날 수 있는지를 구하는 프로그램을 작성하시오. 말이 지나는 칸은 좌측 상단의 칸도 포함된다.

입력

첫째 줄에 R과 C가 빈칸을 사이에 두고 주어진다. (1 ≤ R,C ≤ 20) 둘째 줄부터 R개의 줄에 걸쳐서 보드에 적혀 있는 C개의 대문자 알파벳들이 빈칸 없이 주어진다.

출력

첫째 줄에 말이 지날 수 있는 최대의 칸 수를 출력한다.

내가 푼 풀이

- 문제의 원리는 어렵지않았다.

- (0, 0)에서 시작하여 해당 지점 알파벳이 중복되지않게 최대로 가는 경우의 수를 구하는데 백트래킹이 생각났다.

- 한 쪽으로 알파벳이 중복되지 않을만큼 이동했다가, 중복이된다면 이전 기점으로 돌아와 재탐색한다.

- 처음에는 배열의크기가 최대 20 * 20 이라 단순히 DFS 백트래킹 방식으로 풀면 되겠다 했는데 시간초과가 떴다.

- 탐색하면서 넣는 배열을 확인하는 contains 함수의 시간복잡도가 O(N)이라 여기서 문제인듯 했다.

- Dictionary를 26크기만큼 선언해 알파벳을 만나면 값을 1로 변경하여 중복을 확인하는 방법을 2차로 사용했지만, 이것도 역시 시간초과가 떴다.

- 여기서 더이상 줄일 방법이 떠오르지 않아서 다른 분들이 푼 풀이를 참고했다.

- 백트래킹 DFS 로 푸는방법은 맞았지만, Swift는 시간제한이 빡빡해서 비트마스킹을 사용했다.

- A : 0, B: 1 ... Z: 25 로 지정하고, 알파벳을 만나면 쉬프트 연산자를 이용하여 비트에 넣어준다.

- 다음 알파벳을 비트에 넣은 후 AND 연산을 해서 0이되면, 중복되지 않은 알파벳이고, 0이 아니면 중복된 알파벳으로 판단한다.

- 배열과 딕셔너리를 이용하여 중복알파벳을 판단했는데, 정수 하나로 판단하니 시간이 확 줄어들었다.

- 비트마스킹 기억해두도록 하자.

 

import Foundation

let input = readLine()!.split(separator: " ").map{ Int($0)! }
let r = input[0]
let c = input[1]
var arr = [[Int]]()
var result = 0
let dx = [0,1,0,-1]
let dy = [1,0,-1,0]

// 알파벳 비트마스킹
for i in 0..<r {
    arr.append(readLine()!.map{ Int($0.asciiValue!) - 65 })
}

// DFS 백트래킹
func dfs(x: Int, y: Int, count: Int, alphabet: Int) {
    result = max(result, count)

    for i in 0..<4 {
        let mx = dx[i] + x
        let my = dy[i] + y

        if (mx >= 0 && mx < r) && (my >= 0 && my < c) && (alphabet & 1 << arr[mx][my] == 0) {
            dfs(x: mx, y: my, count: count+1, alphabet: alphabet | 1 << arr[mx][my])
            
        }
    }

}

dfs(x: 0, y: 0, count: 1, alphabet: 1 << arr[0][0])
print(result)

 

문제

UCPC는 '전국 대학생 프로그래밍 대회 동아리 연합 여름 대회'의 줄임말로 알려져있다. 하지만 이 줄임말이 정확히 어떻게 구성되었는지는 아무도 모른다. UCPC 2018을 준비하던 ntopia는 여러 사람들에게 UCPC가 정확히 무엇의 줄임말인지 물어보았지만, 아무도 정확한 답을 제시해주지 못했다. ntopia가 들은 몇 가지 답을 아래에 적어보았다.

  • Union of Computer Programming Contest club contest
  • Union of Computer Programming contest Club contest
  • Union of Computer Programming contest club Contest
  • Union of Collegiate Programming Contest club contest
  • Union of Collegiate Programming contest Club contest
  • Union of Collegiate Programming contest club Contest
  • University Computer Programming Contest
  • University Computer Programming Club contest
  • University Computer Programming club Contest
  • University Collegiate Programming Contest
  • University CPC
  • ...

ntopia는 이렇게 다양한 답을 듣고는 UCPC가 무엇의 약자인지는 아무도 모른다고 결론내렸다. 적당히 슥삭해서 UCPC를 남길 수 있으면 모두 UCPC의 약자인 것이다!

문자열이 주어지면 이 문자열을 적절히 축약해서 "UCPC"로 만들 수 있는지 확인하는 프로그램을 만들어보자.

축약이라는 것은 문자열에서 임의의 문자들을 제거하는 행동을 뜻한다. 예를 들면, "apple"에서 a와 e를 지워 "ppl"로 만들 수 있고, "University Computer Programming Contest"에서 공백과 소문자를 모두 지워 "UCPC"로 만들 수 있다.

문자열을 비교할 때는 대소문자를 구분해 정확히 비교한다. 예를 들어 "UCPC"와 "UCpC"는 다른 문자열이다. 따라서 "University Computer programming Contest"를 "UCPC"로 축약할 수 있는 방법은 없다.

그나저나 UCPC는 정말 무엇의 약자였을까? 정확히 아시는 분은 제보 부탁드립니다.

입력

첫 번째 줄에 알파벳 대소문자, 공백으로 구성된 문자열이 주어진다. 문자열의 길이는 최대 1,000자이다. 문자열의 맨 앞과 맨 끝에 공백이 있는 경우는 없고, 공백이 연속해서 2번 이상 주어지는 경우도 없다.

출력

첫 번째 줄에 입력으로 주어진 문자열을 적절히 축약해 "UCPC"로 만들 수 있으면 "I love UCPC"를 출력하고, 만들 수 없으면 "I hate UCPC"를 출력한다.

내가 푼 풀이

- 주어진 문자열에서 U C P C 차례로 존재하면 I love UCPC 출력, 존재하지 않다면 I hate UCPC 출력한다.

- 이중반복문으로 탐색한다.

 

import Foundation

var str = ["U", "C", "P", "C"]
var arr = readLine()!.split(separator: " ").map{ String($0) }
var idx = 0

for i in arr {
    for j in i {
        let s = String(j)
        if idx < str.count && s == str[idx] {
            idx += 1
        }
    }
}

if idx == 4 {
    print("I love UCPC")
} else {
    print("I hate UCPC")
}

'코딩테스트 > 백준' 카테고리의 다른 글

BOJ-14002 가장 긴 증가하는 부분 수열 4 Swift  (0) 2023.06.29
BOJ-1987 알파벳 Swift  (0) 2023.06.14
BOJ-1890 점프 Swift  (0) 2023.06.13
BOJ-7562 나이트의 이동 Swift  (1) 2023.06.12
BOJ-1041 주사위 Swift  (0) 2023.06.12

문제

N×N 게임판에 수가 적혀져 있다. 이 게임의 목표는 가장 왼쪽 위 칸에서 가장 오른쪽 아래 칸으로 규칙에 맞게 점프를 해서 가는 것이다.

각 칸에 적혀있는 수는 현재 칸에서 갈 수 있는 거리를 의미한다. 반드시 오른쪽이나 아래쪽으로만 이동해야 한다. 0은 더 이상 진행을 막는 종착점이며, 항상 현재 칸에 적혀있는 수만큼 오른쪽이나 아래로 가야 한다. 한 번 점프를 할 때, 방향을 바꾸면 안 된다. 즉, 한 칸에서 오른쪽으로 점프를 하거나, 아래로 점프를 하는 두 경우만 존재한다.

가장 왼쪽 위 칸에서 가장 오른쪽 아래 칸으로 규칙에 맞게 이동할 수 있는 경로의 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 게임 판의 크기 N (4 ≤ N ≤ 100)이 주어진다. 그 다음 N개 줄에는 각 칸에 적혀져 있는 수가 N개씩 주어진다. 칸에 적혀있는 수는 0보다 크거나 같고, 9보다 작거나 같은 정수이며, 가장 오른쪽 아래 칸에는 항상 0이 주어진다.

출력

가장 왼쪽 위 칸에서 가장 오른쪽 아래 칸으로 문제의 규칙에 맞게 갈 수 있는 경로의 개수를 출력한다. 경로의 개수는 263-1보다 작거나 같다.

내가 푼 풀이

- 주어진 N * N 게임판과 같은 크기의 배열 dp를 선언한다.

- (0, 0) 에서 반드시 오른쪽, 아래로만 이동가능하고, 도착점은 (n-1, n-1) 지점이다.

- 이중 반복문은 1행 1열,2열,3열, ... , 2행 1열,2열3열, .... , n행 1열 순으로 순회하므로 이동은 항상 인덱스에서 더해지기 때문에

- 이동한 지점에 이전 dp값을 더하는 방식으로 풀었다.

- dp[i][j] = i행j열까지의 이동한 경로의 수

 

import Foundation

let n = Int(readLine()!)!
var arr = [[Int]]()
var dp = Array(repeating: Array(repeating: 0, count: n), count: n)

// 게임판 입력
for i in 0..<n {
    arr.append(readLine()!.split(separator: " ").map{ Int($0)! })
}
dp[0][0] = 1

// DP
// 이동한지점은 이전지점의 DP값을 더해가는 방식을 사용
for i in 0..<n {
    for j in 0..<n {
        if dp[i][j] != 0 && arr[i][j] != 0 {
            let m = arr[i][j]
            if i+m < n {
                dp[i+m][j] += dp[i][j]
            }
            if j+m < n {
                dp[i][j+m] += dp[i][j]
            }
        }
    }
}

print(dp[n-1][n-1])

'코딩테스트 > 백준' 카테고리의 다른 글

BOJ-1987 알파벳 Swift  (0) 2023.06.14
BOJ-15904 UCPC는 무엇의 약자일까? Swift  (0) 2023.06.13
BOJ-7562 나이트의 이동 Swift  (1) 2023.06.12
BOJ-1041 주사위 Swift  (0) 2023.06.12
BOJ-2096 내려가기 Swift  (0) 2023.06.12

문제

체스판 위에 한 나이트가 놓여져 있다. 나이트가 한 번에 이동할 수 있는 칸은 아래 그림에 나와있다. 나이트가 이동하려고 하는 칸이 주어진다. 나이트는 몇 번 움직이면 이 칸으로 이동할 수 있을까?

입력

입력의 첫째 줄에는 테스트 케이스의 개수가 주어진다.

각 테스트 케이스는 세 줄로 이루어져 있다. 첫째 줄에는 체스판의 한 변의 길이 l(4 ≤ l ≤ 300)이 주어진다. 체스판의 크기는 l × l이다. 체스판의 각 칸은 두 수의 쌍 {0, ..., l-1} × {0, ..., l-1}로 나타낼 수 있다. 둘째 줄과 셋째 줄에는 나이트가 현재 있는 칸, 나이트가 이동하려고 하는 칸이 주어진다.

출력

각 테스트 케이스마다 나이트가 최소 몇 번만에 이동할 수 있는지 출력한다.

내가 푼 풀이

- 주어진 체스판에서 시작점부터 BFS 탐색으로 이동한 곳에 +1 카운트를 해준다.

- 따로 카운트해줄 배열 dp를 선언해 저장하고, 도착점이 목적지랑 같은경우 탐색 종료해준다.

 

import Foundation

let c = Int(readLine()!)!

// 나이트의 이동
var dx = [1, 2, -1, -2, 1, 2, -1, -2]
var dy = [2, 1, -2, -1, -2, -1, 2, 1]

for i in 0..<c {
    var len = Int(readLine()!)!
    var board = Array(repeating: Array(repeating: 0, count: len), count: len)
    var dp = Array(repeating: Array(repeating: 0, count: len), count: len)
    var s = readLine()!.split(separator: " ").map{ Int($0)! }
    var d = readLine()!.split(separator: " ").map{ Int($0)! }
    
    var needVisitQueue = [(x: s[0], y: s[1])]
    
    // BFS
    // 나이트를 움직일때 도착점이 탐색하지 않은곳이라면 +1 카운트
    // 이미 도착했던곳이라면 스킵한다.
    while !needVisitQueue.isEmpty {
        var loc = needVisitQueue.removeFirst()
        if loc.x == d[0] && loc.y == d[1] { break }
        
        for i in 0..<8 {
            var mx = loc.x + dx[i]
            var my = loc.y + dy[i]
            
            if (mx >= 0 && mx < len) && (my >= 0 && my < len) && (dp[mx][my] == 0){
                dp[mx][my] = dp[loc.x][loc.y] + 1
                needVisitQueue.append((x: mx, y: my))
            }
        }
    }
    print(dp[d[0]][d[1]])
}

'코딩테스트 > 백준' 카테고리의 다른 글

BOJ-15904 UCPC는 무엇의 약자일까? Swift  (0) 2023.06.13
BOJ-1890 점프 Swift  (0) 2023.06.13
BOJ-1041 주사위 Swift  (0) 2023.06.12
BOJ-2096 내려가기 Swift  (0) 2023.06.12
BOJ-11725 트리의 부모 찾기 Swift  (1) 2023.06.11

+ Recent posts