-
Dijkstra(최단거리)_programmers_SW2018 배달 [swift]알고리즘/알고리즘 문제풀이 2022. 5. 17. 22:23
https://programmers.co.kr/learn/courses/30/lessons/12978
접근 방법
- 1번 마을에서 2...N번 마을까지, 갈 수 있는지 여부를 재귀를 사용하여 확인한다. -> 1차 코드
- 반복적으로 탐색하는 경로가 생기므로 효율이 떨어짐을 인식.
- 1번 마을에서 k 이하까지, 탐색 가능한 모든 경로를 탐색하여, 방문한 마을을 기록한다. -> 2차 코드.
- 아무리 용을써도 해결 할 수 없는 반례가 있었다....
- Dijkstra 알고리즘 사용... 아래는 코드의 실행순서에 따른 설명이다.
- 매개변수 road를 사용하여 2차원 배열 map을 그린다. 이 떄, i행, j열의 원소 : i번 마을에서 j번 마을로 이동시간. 간선이 없는 경우는 -1로 둔다.
- 1번 마을에서 출발한다. var distance는 1번마을에서 각 마을로 이동하는데 걸리는 최단 시간이다.
- 각 마을로의 접근 시간은 아직 알 수 없으므로, -1로 초기화 한 뒤, distance[0]은 1번 마을에서 1번 마을로 이동이므로, 0이다.
- var queue는 우선순위 queue이다. queue의 원소는 (n번 마을, 걸리는 시간) 1번 마을로부터 시작하므로, queue.append( (0,0 ) )
- 반복문은 queue의 원소가 존재하는 동안, queue는 우선순위 queue이므로, 걸리는 시간 기준으로 sort한다.
- queue의 첫번째 원소를 꺼내어, 해당 원소에 도달하는데, 필요한 시간( input.1 )과 기록된 최단 시간( dintance[input.0] )을 비교하여, 최단 시간이 더 빠르다면, 다음 원소를 꺼내고, 최단 시간이 같거나 느릴 경우, dajikstra(:)를 실행한다.
- map[start]는 start마을에서 각 마을까지의 이동 시간이다. 이 때, index == start는 n마을에서 n마을로 즉, 같은 마을로 이동하는 경우이므로 continue, length <0인 경우는, 마을간의 이동경로가 없는 경우이다.
- distance[index] < 0 인 경우는, 1번 마을에서 index마을까지의 최단 시간이 기재되지 않은 경우이다. 그러므로, 1번 마을에서 start마을 까지의 거리 + start마을에서 index 마을까지의 거리로 할당한다. > distance[index] = distance[start] + length
- distance[index] > distance[start] + length 일 경우, index마을까지의 최단거리 보다, distance[start] + length가 작거나 같으므로, 최단 거리 distance를 갱신하고, index 마을과 연결된 마을의 최단 거리를 확인 하기 위해, queue에 갱신된 정보를 추가한다.
정답 코드
더보기class programmers_2018SWCoding_배달 { var map = [[Int]]() var distance = [Int]() var queue = [(Int, Int)]() func solution(_ N:Int, _ road:[[Int]], _ k:Int) -> Int { makeMap(size: N, road: road) distance = .init(repeating: -1, count: N) distance[0] = 0 queue.append((0, 0)) while !queue.isEmpty { queue.sort(by: { $0.1 < $1.1 }) let input = queue.removeFirst() if distance[input.0] < input.1 { continue } dijkstra(start: input.0) } return distance.filter({ $0 <= k }).count } func makeMap(size: Int, road: [[Int]]) { var tempMap = [[Int]].init(repeating: [Int].init(repeating: -1, count: size), count: size) for info in road { let targetValue = tempMap[info[0]-1][info[1]-1] if targetValue > info[2] || targetValue < 0 { tempMap[info[0]-1][info[1]-1] = info[2] tempMap[info[1]-1][info[0]-1] = info[2] } } map = tempMap for i in 0..<size { map[i][i] = 0 } } func dijkstra(start: Int) { for (index,length) in map[start].enumerated() { if index == start { continue } if length < 0 { continue } if distance[index] < 0 || distance[index] > distance[start] + length { distance[index] = distance[start] + length queue.append((index, length + distance[start])) } } } }
1차 코드
시간 초과
각 목적지에 대해 k시간 이하로 접근 가능한지 확인을 재귀를 사용하여 확인한다.
더보기정확성 테스트테스트 1 〉 통과 (0.37ms, 16.2MB) 테스트 2 〉 통과 (0.12ms, 16.6MB) 테스트 3 〉 통과 (0.20ms, 16.6MB) 테스트 4 〉 통과 (0.17ms, 16.5MB) 테스트 5 〉 통과 (0.34ms, 16.6MB) 테스트 6 〉 통과 (0.17ms, 16.3MB) 테스트 7 〉 통과 (0.24ms, 16.3MB) 테스트 8 〉 통과 (0.18ms, 16.3MB) 테스트 9 〉 통과 (0.09ms, 16.4MB) 테스트 10 〉 통과 (0.37ms, 16.6MB) 테스트 11 〉 통과 (0.39ms, 16.5MB) 테스트 12 〉 통과 (9892.42ms, 16.6MB) 테스트 13 〉 통과 (838.71ms, 16.6MB) 테스트 14 〉 통과 (111.23ms, 16.8MB) 테스트 15 〉 통과 (1698.04ms, 17MB) 테스트 16 〉 통과 (896.62ms, 16.6MB) 테스트 17 〉 실패 (시간 초과) 테스트 18 〉 통과 (313.59ms, 16.4MB) 테스트 19 〉 통과 (225.69ms, 16.9MB) 테스트 20 〉 실패 (시간 초과) 테스트 21 〉 통과 (812.57ms, 17.1MB) 테스트 22 〉 통과 (3862.06ms, 16.7MB) 테스트 23 〉 실패 (시간 초과) 테스트 24 〉 실패 (시간 초과) 테스트 25 〉 실패 (시간 초과) 테스트 26 〉 통과 (1031.06ms, 17.2MB) 테스트 27 〉 실패 (시간 초과) 테스트 28 〉 실패 (시간 초과) 테스트 29 〉 실패 (시간 초과) 테스트 30 〉 통과 (418.19ms, 17.2MB) 테스트 31 〉 실패 (시간 초과) 테스트 32 〉 실패 (시간 초과) 채점 결과정확성: 68.8합계: 68.8 / 100.0더보기class programmers_2018SWCoding_배달 { struct RoadInfo { let lhs : Int let rhs : Int let length : Int func other(_ with: Int) -> Int { if with == lhs { return rhs } return lhs } } var infos = [RoadInfo]() func transform2Info(_ road: [[Int]]) -> [RoadInfo] { var sol = [RoadInfo]() for info in road { let tmp = RoadInfo.init(lhs: info[0], rhs: info[1], length: info[2]) sol.append(tmp) } return sol } func solution(_ N:Int, _ road:[[Int]], _ k:Int) -> Int { var answer = 0 infos = transform2Info(road) for n in 1...N { if true == recursion(destination: n, nowPosition: 1, limit: k, movedLength: 0) { print(n) answer += 1 } } return answer } func recursion(destination: Int, nowPosition: Int, limit: Int, movedLength: Int) -> Bool { if movedLength > limit { return false } if destination == nowPosition { return true } let nextList = infos.filter({ info in info.lhs == nowPosition || info.rhs == nowPosition }) for next in nextList { // print(destination, nowPosition, limit, movedLength, next.other(nowPosition), next.length) if true == recursion(destination: destination, nowPosition: next.other(nowPosition), limit: limit, movedLength: movedLength + next.length) { return true } } return false } /* N road K result 5 [[1,2,1],[2,3,3],[5,2,2],[1,4,2],[5,3,1],[5,4,2]] 3 4 6 [[1,2,1],[1,3,2],[2,3,2],[3,4,3],[3,5,2],[3,5,3],[5,6,1]] 4 4 */ }
2차 코드
처음부터 끝까지 가면서 방문한 곳을 기록한다.
recursion마다 history를 사용하여, 방문한 곳을 다시 방문하는 경우를 제외할 수 있으며, 서로 다른 경우를 탐색 할 수 있다.
하나의 테스트가 timeout.
처음부터 끝까지 탐색하는데 오랜 시간이 걸리는 경우가 반례인 것으로 추정
더보기정확성 테스트테스트 1 〉 통과 (0.36ms, 16.4MB) 테스트 2 〉 통과 (0.43ms, 16.4MB) 테스트 3 〉 통과 (0.69ms, 16.5MB) 테스트 4 〉 통과 (0.18ms, 16.4MB) 테스트 5 〉 통과 (0.10ms, 16.5MB) 테스트 6 〉 통과 (0.15ms, 16.6MB) 테스트 7 〉 통과 (0.16ms, 16.2MB) 테스트 8 〉 통과 (0.10ms, 16.5MB) 테스트 9 〉 통과 (0.11ms, 16.5MB) 테스트 10 〉 통과 (0.17ms, 16.1MB) 테스트 11 〉 통과 (0.20ms, 16.5MB) 테스트 12 〉 통과 (0.39ms, 16.4MB) 테스트 13 〉 통과 (0.41ms, 16.3MB) 테스트 14 〉 통과 (4.98ms, 16.6MB) 테스트 15 〉 통과 (37.42ms, 16.7MB) 테스트 16 〉 통과 (0.25ms, 16.4MB) 테스트 17 〉 통과 (0.21ms, 16.6MB) 테스트 18 〉 통과 (0.80ms, 16.6MB) 테스트 19 〉 통과 (1.97ms, 17MB) 테스트 20 〉 통과 (2.05ms, 16.6MB) 테스트 21 〉 통과 (6.97ms, 16.8MB) 테스트 22 〉 통과 (3.66ms, 16.5MB) 테스트 23 〉 통과 (26.35ms, 16.8MB) 테스트 24 〉 통과 (118.54ms, 16.6MB) 테스트 25 〉 통과 (7.93ms, 16.9MB) 테스트 26 〉 통과 (14.57ms, 17.1MB) 테스트 27 〉 통과 (36.18ms, 17.2MB) 테스트 28 〉 통과 (23.01ms, 16.8MB) 테스트 29 〉 통과 (109.60ms, 17.1MB) 테스트 30 〉 통과 (21.67ms, 17.2MB) 테스트 31 〉 통과 (0.55ms, 16.6MB) 테스트 32 〉 실패 (시간 초과) 채점 결과정확성: 96.9합계: 96.9 / 100.0더보기struct RoadInfo { let lhs : Int let rhs : Int let length : Int func other(_ with: Int) -> Int { if with == lhs { return rhs } return lhs } } var infos = [RoadInfo]() func transform2Info(_ road: [[Int]]) -> [RoadInfo] { var sol = [RoadInfo]() for info in road { let tmp = RoadInfo.init(lhs: info[0], rhs: info[1], length: info[2]) sol.append(tmp) } return sol } func solution(_ N:Int, _ road:[[Int]], _ k:Int) -> Int { var answer = 0 infos = transform2Info(road) recursion(nowPosition: 1, limit: k, movedLength: 0, boundary: N) return visited.count } var visited = Set<Int>() func recursion(nowPosition: Int, limit: Int, movedLength: Int, boundary: Int, history: Set<Int> = []) { if movedLength > limit { visited = visited.union(history) return } var newHistory = history if !newHistory.insert(nowPosition).inserted { visited = visited.union(history) return } // let newBoundary = Set<Int>.init(1...boundary).subtracting(newHistory) let nextList = infos.filter({ info in info.lhs == nowPosition || info.rhs == nowPosition }) // .filter({ info in // !(newHistory.contains(info.lhs) && newHistory.contains(info.rhs)) // }) // .filter({ info in // newBoundary.contains(info.rhs) || newBoundary.contains(info.lhs) // }) for next in nextList { recursion(nowPosition: next.other(nowPosition), limit: limit, movedLength: movedLength + next.length, boundary: boundary, history: newHistory) } }
'알고리즘 > 알고리즘 문제풀이' 카테고리의 다른 글
Dijkstra(최단거리)_programmers_2021kakao 합승택시요금[swift] (0) 2022.05.18 문자열_프로그래머스_시저 암호 [swift] (0) 2022.05.18 Stack_프로그래머스_괄호 회전하기 [swift] (0) 2022.05.11 LinkedList_LeetCode_19. Remove Nth Node From End of List [swift] (0) 2022.05.11 행렬_LeetCode_200. Number of island swift (0) 2022.02.28 - 1번 마을에서 2...N번 마을까지, 갈 수 있는지 여부를 재귀를 사용하여 확인한다. -> 1차 코드