문제

여러분도 알다시피 여러분의 프린터 기기는 여러분이 인쇄하고자 하는 문서를 인쇄 명령을 받은 ‘순서대로’, 즉 먼저 요청된 것을 먼저 인쇄한다. 여러 개의 문서가 쌓인다면 Queue 자료구조에 쌓여서 FIFO - First In First Out - 에 따라 인쇄가 되게 된다. 하지만 상근이는 새로운 프린터기 내부 소프트웨어를 개발하였는데, 이 프린터기는 다음과 같은 조건에 따라 인쇄를 하게 된다.

  1. 현재 Queue의 가장 앞에 있는 문서의 ‘중요도’를 확인한다.
  2. 나머지 문서들 중 현재 문서보다 중요도가 높은 문서가 하나라도 있다면, 이 문서를 인쇄하지 않고 Queue의 가장 뒤에 재배치 한다. 그렇지 않다면 바로 인쇄를 한다.

예를 들어 Queue에 4개의 문서(A B C D)가 있고, 중요도가 2 1 4 3 라면 C를 인쇄하고, 다음으로 D를 인쇄하고 A, B를 인쇄하게 된다.

여러분이 할 일은, 현재 Queue에 있는 문서의 수와 중요도가 주어졌을 때, 어떤 한 문서가 몇 번째로 인쇄되는지 알아내는 것이다. 예를 들어 위의 예에서 C문서는 1번째로, A문서는 3번째로 인쇄되게 된다.

입력

첫 줄에 테스트케이스의 수가 주어진다. 각 테스트케이스는 두 줄로 이루어져 있다.

테스트케이스의 첫 번째 줄에는 문서의 개수 N(1 ≤ N ≤ 100)과, 몇 번째로 인쇄되었는지 궁금한 문서가 현재 Queue에서 몇 번째에 놓여 있는지를 나타내는 정수 M(0 ≤ M < N)이 주어진다. 이때 맨 왼쪽은 0번째라고 하자. 두 번째 줄에는 N개 문서의 중요도가 차례대로 주어진다. 중요도는 1 이상 9 이하의 정수이고, 중요도가 같은 문서가 여러 개 있을 수도 있다.

출력

각 테스트 케이스에 대해 문서가 몇 번째로 인쇄되는지 출력한다.

내가 푼 풀이

- 큐를 구현하고, 위 동작들을 메서드로 구현해준다.

 

import Foundation

// 입력받기
let T = Int(readLine()!)!

for i in 0..<T {
    let input = readLine()!.split(separator: " ").map{Int(String($0))!}
    let N = input[0], M = input[1]
    var arr = readLine()!.split(separator: " ").map{Int(String($0))!}
    var queue = [(idx: Int, ipt: Int)]()
    var count = 0
    for j in 0..<arr.count {
        queue.append((idx: j, ipt: arr[j]))
    }
    // 궁금한 문서 뽑을때까지 반복
    while true {
        if isImportant(queue) {
            count += 1
            if pop(&queue).idx == M { break }
        } else {
            moveBack(&queue)
        }
    }
    print(count)
}

// 첫번째원소가 중요도가 가장 높은지 파악
func isImportant(_ queue: [(idx: Int, ipt: Int)]) -> Bool {
    guard let top = top(queue) else { return false }
    var max = Int.min
    for i in queue {
        if i.ipt > max { max = i.ipt }
    }
    if max == top.ipt { return true } else { return false }
}
// 첫번째 원소 출력
func top(_ queue: [(idx: Int, ipt: Int)]) -> (idx: Int, ipt: Int)? {
    if queue.isEmpty { return nil }
    return queue[0]
}
// 첫번째 원소 Pop
func pop(_ queue: inout [(idx: Int, ipt: Int)]) -> (idx: Int, ipt: Int) {
    var a = Array(queue.reversed())
    let num = a.removeLast()
    queue = Array(a.reversed())
    return num
}
// 첫번째 원소 뒤로 이동
func moveBack(_ queue: inout [(idx: Int, ipt: Int)]) {
    let num = pop(&queue)
    queue.append(num)
}

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

BOJ-1717 집합의 표현 Swift  (0) 2024.04.28
BOJ-2493 탑 Swift  (1) 2024.04.28
BOJ-2473 세 용액 Swift  (0) 2024.04.27
BOJ-2343 기타 레슨 Swift  (1) 2024.04.27
BOJ-2458 키 순서 Swift  (1) 2024.04.25

문제

KOI 부설 과학연구소에서는 많은 종류의 산성 용액과 알칼리성 용액을 보유하고 있다. 각 용액에는 그 용액의 특성을 나타내는 하나의 정수가 주어져있다.  산성 용액의 특성값은 1부터 1,000,000,000까지의 양의 정수로 나타내고, 알칼리성 용액의 특성값은 -1부터 -1,000,000,000까지의 음의 정수로 나타낸다.

같은 양의 세 가지 용액을 혼합한 용액의 특성값은 혼합에 사용된 각 용액의 특성값의 합으로 정의한다. 이 연구소에서는 같은 양의 세 가지 용액을 혼합하여 특성값이 0에 가장 가까운 용액을 만들려고 한다. 

예를 들어, 주어진 용액들의 특성값이 [-2, 6, -97, -6, 98]인 경우에는 특성값이 -97와 -2인 용액과 특성값이 98인 용액을 혼합하면 특성값이 -1인 용액을 만들 수 있고, 이 용액이 특성값이 0에 가장 가까운 용액이다. 참고로, 세 종류의 알칼리성 용액만으로나 혹은 세 종류의 산성 용액만으로 특성값이 0에 가장 가까운 혼합 용액을 만드는 경우도 존재할 수 있다.

산성 용액과 알칼리성 용액이 주어졌을 때, 이 중 같은 양의 세 개의 서로 다른 용액을 혼합하여 특성값이 0에 가장 가까운 용액을 만들어내는 세 용액을 찾는 프로그램을 작성하시오.

입력

첫째 줄에는 전체 용액의 수 N이 입력된다. N은 3 이상 5,000 이하의 정수이다. 둘째 줄에는 용액의 특성값을 나타내는 N개의 정수가 빈칸을 사이에 두고 주어진다. 이 수들은 모두 -1,000,000,000 이상 1,000,000,000 이하이다. N개의 용액들의 특성값은 모두 다르고, 산성 용액만으로나 알칼리성 용액만으로 입력이 주어지는 경우도 있을 수 있다.

출력

첫째 줄에 특성값이 0에 가장 가까운 용액을 만들어내는 세 용액의 특성값을 출력한다. 출력해야하는 세 용액은 특성값의 오름차순으로 출력한다. 특성값이 0에 가장 가까운 용액을 만들어내는 경우가 두 개 이상일 경우에는 그 중 아무것이나 하나를 출력한다.

내가 푼 풀이

접근방법: 이분탐색

이전에 두용액문제를 풀고왔더니 너무 쉽게 파악해버렸다

두 용액을 섞을땐 용액의수가 최대 10만이였는데 이 문제에선 최대 5000 이다.

단순히 두 용액을 고정하고 한 용액을 순회하며 섞어봐도 정답은 나오지만 5000^3 = 1250억번 연산이므로 시간초과가 무조건 뜰것이다.

그래서 두개의 용액을 고정하고 한 용액을 구할때 이분탐색을 이용했다.

시간복잡도는 O(N^2logN)으로 이문제에선 약 9700만번연산(1억보다 적다)으로 시간안에 풀어졌다.

 

이분탐색

용액을 섞었을때, 음수면 start인덱스가 mid+1 양수면 end인덱스가 mid-1 해주어 0과 가장 가깝게 만든다.

 

코드로 구현하면 다음과 같다.

import Foundation

// 입력받기
let N = Int(readLine()!)!
var arr = readLine()!.split(separator: " ").map{Int(String($0))!}
var minSum = Int.max
var answer = [Int]()
// 이분탐색 사용을위해 정렬
arr.sort(by: <)


// 이분탐색
for i in 0..<N {
    for j in i+1..<N {
        // 두용액을 고정한다.
        let first = arr[i]
        let second = arr[j]
        let sum = first + second
        var s = j+1
        var e = N-1
        // 나머지 한용액을 이분탐색을 이용해 구해준다.
        while s <= e {
            let m = (s+e)/2
            let total = sum + arr[m]
            // 0이되면 바로 값 갱신 후 탈출
            if total == 0 {
                answer = [arr[i],arr[j],arr[m]]
                minSum = total
                break
            }
            // 합쳐진 용액이 음수면 나머지 용액을 더 큰수로, 양수면 더 작은수로
            if total < 0 {
                s = m+1
            } else {
                e = m-1
            }
            // 최솟값이 나오면 항상 값을 갱신해준다.
            if minSum > abs(total) {
                minSum = abs(total)
                answer = [arr[i],arr[j],arr[m]]
            }
        }
        
    }
}

print("\(answer[0]) \(answer[1]) \(answer[2])")

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

BOJ-2493 탑 Swift  (1) 2024.04.28
BOJ-1966 프린터 큐 Swift  (1) 2024.04.28
BOJ-2343 기타 레슨 Swift  (1) 2024.04.27
BOJ-2458 키 순서 Swift  (1) 2024.04.25
BOJ-11404 플로이드 Swift  (0) 2024.04.25

문제

강토는 자신의 기타 강의 동영상을 블루레이로 만들어 판매하려고 한다. 블루레이에는 총 N개의 강의가 들어가는데, 블루레이를 녹화할 때, 강의의 순서가 바뀌면 안 된다. 순서가 뒤바뀌는 경우에는 강의의 흐름이 끊겨, 학생들이 대혼란에 빠질 수 있기 때문이다. 즉, i번 강의와 j번 강의를 같은 블루레이에 녹화하려면 i와 j 사이의 모든 강의도 같은 블루레이에 녹화해야 한다.

강토는 이 블루레이가 얼마나 팔릴지 아직 알 수 없기 때문에, 블루레이의 개수를 가급적 줄이려고 한다. 오랜 고민 끝에 강토는 M개의 블루레이에 모든 기타 강의 동영상을 녹화하기로 했다. 이때, 블루레이의 크기(녹화 가능한 길이)를 최소로 하려고 한다. 단, M개의 블루레이는 모두 같은 크기이어야 한다.

강토의 각 강의의 길이가 분 단위(자연수)로 주어진다. 이때, 가능한 블루레이의 크기 중 최소를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 강의의 수 N (1 ≤ N ≤ 100,000)과 M (1 ≤ M ≤ N)이 주어진다. 다음 줄에는 강토의 기타 강의의 길이가 강의 순서대로 분 단위로(자연수)로 주어진다. 각 강의의 길이는 10,000분을 넘지 않는다.

출력

첫째 줄에 가능한 블루레이 크기중 최소를 출력한다.

내가 푼 풀이

접근방법: 이분탐색

- 담을수 있는 블루레이의 길이는 최소 1부터 최대 기타강의의 모든길이를 더한값이 된다.

- 이 범위를 이분탐색으로 최소크기를 구한다.

 

 

주의해야할 점)

- 기타강의는 순서가 바뀌면안되므로 주어진 순서를 지켜야한다.

- 블루레이의 크기가 기타강의 한개의 길이보다 작을 수 있다.

- 기타강의를 빼먹지않고 모두 블루레이에 담아야한다.

 

코드로 구현하면 다음과 같다.

import Foundation

// 입력받기
let input = readLine()!.split(separator: " ").map{Int(String($0))!}
let N = input[0], M = input[1]
var arr = readLine()!.split(separator: " ").map{Int(String($0))!}
var start = 1
var end = arr.reduce(0, +)

// 이분탐색
while start <= end {
    // 이분탐색의 중간값
    let mid = (start + end) / 2
    var sum = 0
    var count = 0
    var left = 0
    var pass = false
    
    // 블루레이에 강의를 최대한 담아본다.
    // 강의길이가 mid값보다 크다면 아에 담을 수 없으므로 이 길이는 사용할 수 없다 (더 길게해서 담아봐야함)
    while left < arr.count {
        sum += arr[left]
        if sum > mid {
            let num = sum - arr[left]
            if num != 0 && num <= mid {
                count += 1
            } else {
                pass = true
                break
            }
            sum = arr[left]
        }
        left += 1
    }
    // 최대한 담아보고 남은 강의가 있고, mid보다 작다면 블루레이에 담을 수 있으므로 +1
    // 남은 강의가 있지만 블루레이보다 크다면 담을 수 없다. pass = true
    if sum != 0 && sum <= mid {
        count += 1
    } else {
        pass = true
    }
    // 블루레이 크기가 너무작아 모든강의를 담을 수 없었다면 블루레이 크기를 늘린다.
    if pass {
        start = mid + 1
    } else {
        // 모든 강의를 담아봤지만 정한 갯수보다 작거나 같다면 갯수를 늘리기위해 블루레이 크기를 줄인다.
        // 모든 강의를 담아봤지만 정한 갯수보다 크다면 갯수를 줄이기위해 블루레이 크기를 늘린다.
        if count <= M {
            end = mid - 1
        } else {
            start = mid + 1
        }
    }
}
print(start)

 

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

BOJ-1966 프린터 큐 Swift  (1) 2024.04.28
BOJ-2473 세 용액 Swift  (0) 2024.04.27
BOJ-2458 키 순서 Swift  (1) 2024.04.25
BOJ-11404 플로이드 Swift  (0) 2024.04.25
BOJ-4485 녹색 옷 입은 애가 젤다지? Swift  (0) 2024.04.24

문제

1번부터 N번까지 번호가 붙여져 있는 학생들에 대하여 두 학생끼리 키를 비교한 결과의 일부가 주어져 있다. 단, N명의 학생들의 키는 모두 다르다고 가정한다. 예를 들어, 6명의 학생들에 대하여 6번만 키를 비교하였고, 그 결과가 다음과 같다고 하자.

  • 1번 학생의 키 < 5번 학생의 키
  • 3번 학생의 키 < 4번 학생의 키
  • 5번 학생의 키 < 4번 학생의 키
  • 4번 학생의 키 < 2번 학생의 키
  • 4번 학생의 키 < 6번 학생의 키
  • 5번 학생의 키 < 2번 학생의 키

이 비교 결과로부터 모든 학생 중에서 키가 가장 작은 학생부터 자신이 몇 번째인지 알 수 있는 학생들도 있고 그렇지 못한 학생들도 있다는 사실을 아래처럼 그림을 그려 쉽게 확인할 수 있다. a번 학생의 키가 b번 학생의 키보다 작다면, a에서 b로 화살표를 그려서

표현하였다.

 

1번은 5번보다 키가 작고, 5번은 4번보다 작기 때문에, 1번은 4번보다 작게 된다. 그러면 1번, 3번, 5번은 모두 4번보다 작게 된다. 또한 4번은 2번과 6번보다 작기 때문에, 4번 학생은 자기보다 작은 학생이 3명이 있고, 자기보다 큰 학생이 2명이 있게 되어 자신의 키가 몇 번째인지 정확히 알 수 있다. 그러나 4번을 제외한 학생들은 자신의 키가 몇 번째인지 알 수 없다.

학생들의 키를 비교한 결과가 주어질 때, 자신의 키가 몇 번째인지 알 수 있는 학생들이 모두 몇 명인지 계산하여 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 학생들의 수 N (2 ≤ N ≤ 500)과 두 학생 키를 비교한 횟수 M (0 ≤ M ≤ N(N-1)/2)이 주어진다.

다음 M개의 각 줄에는 두 학생의 키를 비교한 결과를 나타내는 N보다 작거나 같은 서로 다른 양의 정수 a와 b가 주어진다. 이는 번호가 a인 학생이 번호가 b인 학생보다 키가 작은 것을 의미한다.

출력

자신이 키가 몇 번째인지 알 수 있는 학생이 모두 몇 명인지를 출력한다.

내가 푼 풀이

접근방법: 플로이드-워셜 알고리즘

1. 예시 그래프처럼 비교한것을 그래프의 인접행렬로 담는다.

2. 플로이드-워셜 알고리즘을 이용한다면, 모든 노드쌍의 최단거리가 나온다.

3. 인접행렬 graph의 graph[i][j] 값의 의미는 i번이 j번보다 작다는 뜻이다, 반대로 graph[j][i]는 j는 i보다 작다는 의미다.

4. 본인을 제외한 나머지 인원이 작거나 큰 인원으로 알 수 있다면, 순서를 알 수 있다.

 

ex) 인접행렬에서 1번의 순서를 아는방법

garph[1][j]에 값이 있다면, 1번은 j보다 작다는 의미이고 값이 있는만큼 1번이 작다는 것이다.

graph[j][i]에 값이 있다면, 1번은 j보다 크다는 의미이고, 값이 있는만큼 1번이 크다는 것이다.

위의 두 값을 더해서 인원수-1 이라면, 순서를 알 수 있다!

 

인원수는 최대 500이라 플로이드-워셜알고리즘을 사용하면 1억2500만번 연산을 한다.

이 문제에서는 단순히 큰지 작은지 판단만 가능하면 되므로 bool값을 이용해 시간을 단축한다.

 

코드로 구현하면 다음과 같다.

 

import Foundation

// 입력
let input = readLine()!.split(separator: " ").map{Int(String($0))!}
let N = input[0], M = input[1]
var graph = Array(repeating: Array(repeating: false, count: N+1), count: N+1)
var answer = 0

// 그래프 입력
for i in 0..<M {
    let cp = readLine()!.split(separator: " ").map{Int(String($0))!}
    graph[cp[0]][cp[1]] = true
}
// 플로이드-워셜
// 단순히 자신보다 크거나 작은 인원이 있는지만 알기 위해 Bool값 이용(정수 이용하면 시간이 너무 오래걸린다.)
for m in 1...N {
    for i in 1...N {
        for j in 1...N {
            if graph[i][m] && graph[m][j] { graph[i][j] = true }
        }
    }
}

// 해당 번호보다 (더작은인원 + 더큰인원 = 본인제외한 인원수) 라면 순서를 알 수 있다.
for i in 1...N {
    var tall = 0
    var short = 0
    for j in 1...N {
        if i == j { continue }
        if graph[i][j] { tall += 1 }
        if graph[j][i] { short += 1 }
    }
    if tall+short == N-1 {
        answer += 1
    }
}

print(answer)

문제

n(2 ≤ n ≤ 100)개의 도시가 있다. 그리고 한 도시에서 출발하여 다른 도시에 도착하는 m(1 ≤ m ≤ 100,000)개의 버스가 있다. 각 버스는 한 번 사용할 때 필요한 비용이 있다.

모든 도시의 쌍 (A, B)에 대해서 도시 A에서 B로 가는데 필요한 비용의 최솟값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 도시의 개수 n이 주어지고 둘째 줄에는 버스의 개수 m이 주어진다. 그리고 셋째 줄부터 m+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그 버스의 출발 도시의 번호가 주어진다. 버스의 정보는 버스의 시작 도시 a, 도착 도시 b, 한 번 타는데 필요한 비용 c로 이루어져 있다. 시작 도시와 도착 도시가 같은 경우는 없다. 비용은 100,000보다 작거나 같은 자연수이다.

시작 도시와 도착 도시를 연결하는 노선은 하나가 아닐 수 있다.

출력

n개의 줄을 출력해야 한다. i번째 줄에 출력하는 j번째 숫자는 도시 i에서 j로 가는데 필요한 최소 비용이다. 만약, i에서 j로 갈 수 없는 경우에는 그 자리에 0을 출력한다.

내가 푼 풀이

접근방법: 플로이드 워셜 알고리즘

- 플로이드 워셜 알고리즘을 이용해 모든노드쌍의 최단거리를 구한다.

- 이때 최댓값과, 출력할때 갈수 없는경우를 0출력하는것을 주의한다.

 

import Foundation

// 입력
let n = Int(readLine()!)!
let m = Int(readLine()!)!
var graph = Array(repeating: Array(repeating: 1000000000, count: n+1), count: n+1)

// 인접행렬
for _ in 0..<m {
    let bus = readLine()!.split(separator: " ").map{Int(String($0))!}
    if graph[bus[0]][bus[1]] > bus[2] {
        graph[bus[0]][bus[1]] = bus[2]
    }
}
// i도시는 i도시로 갈 수 없다.
for i in 1...n {
    graph[i][i] = 0
}

// 플로이드-워셜 알고리즘
for m in 1...n {
    for i in 1...n {
        for j in 1...n {
            let im = graph[i][m]
            let mj = graph[m][j]
            graph[i][j] = min(graph[i][j], graph[i][m] + graph[m][j])
        }
    }
}

// 최단거리를 구하기위해 비교값으로 큰 값을 주었는데, 이동할 수 없음을 0으로 변환
for i in 0..<graph.count {
    for j in 0..<graph[0].count {
        if graph[i][j] == 1000000000 { graph[i][j] = 0 }
    }
}

// 출력
for i in 1..<graph.count {
    var answer = ""
    for j in 1..<graph[i].count {
        if j == graph[i].count - 1 {
            answer += "\(graph[i][j])"
        } else {
            answer += "\(graph[i][j]) "
        }
    }
    print(answer)
}

문제

젤다의 전설 게임에서 화폐의 단위는 루피(rupee)다. 그런데 간혹 '도둑루피'라 불리는 검정색 루피도 존재하는데, 이걸 획득하면 오히려 소지한 루피가 감소하게 된다!

젤다의 전설 시리즈의 주인공, 링크는 지금 도둑루피만 가득한 N x N 크기의 동굴의 제일 왼쪽 위에 있다. [0][0]번 칸이기도 하다. 왜 이런 곳에 들어왔냐고 묻는다면 밖에서 사람들이 자꾸 "젤다의 전설에 나오는 녹색 애가 젤다지?"라고 물어봤기 때문이다. 링크가 녹색 옷을 입은 주인공이고 젤다는 그냥 잡혀있는 공주인데, 게임 타이틀에 젤다가 나와있다고 자꾸 사람들이 이렇게 착각하니까 정신병에 걸릴 위기에 놓인 것이다.

하여튼 젤다...아니 링크는 이 동굴의 반대편 출구, 제일 오른쪽 아래 칸인 [N-1][N-1]까지 이동해야 한다. 동굴의 각 칸마다 도둑루피가 있는데, 이 칸을 지나면 해당 도둑루피의 크기만큼 소지금을 잃게 된다. 링크는 잃는 금액을 최소로 하여 동굴 건너편까지 이동해야 하며, 한 번에 상하좌우 인접한 곳으로 1칸씩 이동할 수 있다.

링크가 잃을 수밖에 없는 최소 금액은 얼마일까?

입력

입력은 여러 개의 테스트 케이스로 이루어져 있다.

각 테스트 케이스의 첫째 줄에는 동굴의 크기를 나타내는 정수 N이 주어진다. (2 ≤ N ≤ 125) N = 0인 입력이 주어지면 전체 입력이 종료된다.

이어서 N개의 줄에 걸쳐 동굴의 각 칸에 있는 도둑루피의 크기가 공백으로 구분되어 차례대로 주어진다. 도둑루피의 크기가 k면 이 칸을 지나면 k루피를 잃는다는 뜻이다. 여기서 주어지는 모든 정수는 0 이상 9 이하인 한 자리 수다.

출력

각 테스트 케이스마다 한 줄에 걸쳐 정답을 형식에 맞춰서 출력한다. 형식은 예제 출력을 참고하시오.

내가 푼 풀이

접근방법: 다익스트라

다익스트라 알고리즘을 이용하여 선형탐색을 한다면, 시간초과가 난다.

따라서 다익스트라 알고리즘을 이용하되, 최단거리가 가장 짧은(여기선 루피가 적은)것 먼저 우선순위로 고른다.

현재 위치에서 상하좌우로 이동가능하니, 현재위치의 인접한노드는 상하좌우로 한다.

 

코드로 구현하면 다음과 같다.

import Foundation

var index = 1
while true {
    // 입력받기
    let N = Int(readLine()!)!
    if N == 0 { break }
    var graph = [[Int]]()
    var md = Array(repeating: Array(repeating: 1000000, count: N), count: N)
    for i in 0..<N {
        graph.append(readLine()!.split(separator: " ").map{Int(String($0))!})
    }
    md[0][0] = graph[0][0]
    var queue = [(x: 0, y: 0, cost: graph[0][0])]
    var dx = [0,1,0,-1]
    var dy = [1,0,-1,0]
    
    // 다익스트라
    while !queue.isEmpty {
        let node = queue.removeLast()
        // 맨 오른쪽아래에 도달하면 멈춤
        // 최단거리가 가장 적은것 우선순위로 탐색하기때문에, 멈춰도된다. 선형탐색이라면 정답이 아닐수도 있다.
        if node.x == N-1 && node.y == N-1 { break }
        // 인접한 위치들 선택
        for i in 0..<4 {
            let mx = node.x + dx[i]
            let my = node.y + dy[i]
            if mx >= N || mx < 0 || my >= N || my < 0 { continue }
            if md[my][mx] > md[node.y][node.x] + graph[my][mx]{
                md[my][mx] = md[node.y][node.x] + graph[my][mx]
                queue.append((x: mx, y: my, cost: md[my][mx]))
            }
        }
        // 큐의 우선순위를 루피가 가장적은것 우선(배열의 마지막을 뽑기때문에 내림차순으로 한다.)
        queue.sort{ $0.cost > $1.cost }
    }
    print("Problem \(index): \(md[N-1][N-1])")
    index += 1
}

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

BOJ-2458 키 순서 Swift  (1) 2024.04.25
BOJ-11404 플로이드 Swift  (0) 2024.04.25
BOJ-18352 특정 거리의 도시 찾기 Swift  (0) 2024.04.24
BOJ-1916 최소비용 구하기 Swift  (0) 2024.04.24
BOJ-1027 고층 건물 Swift  (0) 2024.04.23

문제

어떤 나라에는 1번부터 N번까지의 도시와 M개의 단방향 도로가 존재한다. 모든 도로의 거리는 1이다.

이 때 특정한 도시 X로부터 출발하여 도달할 수 있는 모든 도시 중에서, 최단 거리가 정확히 K인 모든 도시들의 번호를 출력하는 프로그램을 작성하시오. 또한 출발 도시 X에서 출발 도시 X로 가는 최단 거리는 항상 0이라고 가정한다.

예를 들어 N=4, K=2, X=1일 때 다음과 같이 그래프가 구성되어 있다고 가정하자.

이 때 1번 도시에서 출발하여 도달할 수 있는 도시 중에서, 최단 거리가 2인 도시는 4번 도시 뿐이다.  2번과 3번 도시의 경우, 최단 거리가 1이기 때문에 출력하지 않는다.

입력

첫째 줄에 도시의 개수 N, 도로의 개수 M, 거리 정보 K, 출발 도시의 번호 X가 주어진다. (2 ≤ N ≤ 300,000, 1 ≤ M ≤ 1,000,000, 1 ≤ K ≤ 300,000, 1 ≤ X ≤ N) 둘째 줄부터 M개의 줄에 걸쳐서 두 개의 자연수 A, B가 공백을 기준으로 구분되어 주어진다. 이는 A번 도시에서 B번 도시로 이동하는 단방향 도로가 존재한다는 의미다. (1 ≤ A, B ≤ N) 단, A와 B는 서로 다른 자연수이다.

출력

X로부터 출발하여 도달할 수 있는 도시 중에서, 최단 거리가 K인 모든 도시의 번호를 한 줄에 하나씩 오름차순으로 출력한다.

이 때 도달할 수 있는 도시 중에서, 최단 거리가 K인 도시가 하나도 존재하지 않으면 -1을 출력한다.

내가 푼 풀이

접근방법: 다익스트라

처음에 그래프를 저장할 때 인접행렬로 저장했다가, 30만 x 30만이라서  메모리 초과가 일어났다.

인접리스트로 그래프를 저장하고 다익스트라 알고리즘을 이용한다.

다익스트라는 선형탐색으로 하였지만 겨우 시간초과에 안맞게 통과된거같다.

import Foundation

// 입력받기
let input = readLine()!.split(separator: " ").map{Int(String($0))!}
let N = input[0], M = input[1], K = input[2], X = input[3]
var md = Array(repeating: 1000000, count: N+1)
var graph = [Int: [Int]]()

// 인접리스트
for i in 0..<M {
    let road = readLine()!.split(separator: " ").map{Int(String($0))!}
    if graph[road[0]] == nil {
        graph[road[0]] = [road[1]]
    } else {
        graph[road[0]]!.append(road[1])
    }
}

var queue = [(cur: X, cost: 0)]
md[X] = 0
// 다익스트라
while !queue.isEmpty {
    let node = queue.removeLast()
    var next = graph[node.cur]
    // 그다음 인접노드가 없거나 이미 K보다 크다면 의미가 없으므로 건너뜀
    if next == nil || node.cost > K { continue }
    // 인접한 노드들의 최단거리정보를 저장하고, 다음 노드 탐색
    for i in next! {
        if md[i] > md[node.cur] + 1 {
            md[i] = md[node.cur] + 1
            queue.append((cur: i, cost:md[i]))
        }
    }
    
}

// 출력
var find = false
for i in 0..<md.count {
    if md[i] == K {
        find = true
        print(i)
    }
}

if !find {
    print(-1)
}

 

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

BOJ-11404 플로이드 Swift  (0) 2024.04.25
BOJ-4485 녹색 옷 입은 애가 젤다지? Swift  (0) 2024.04.24
BOJ-1916 최소비용 구하기 Swift  (0) 2024.04.24
BOJ-1027 고층 건물 Swift  (0) 2024.04.23
BOJ-3190 뱀 Swift  (0) 2024.04.22

문제

N개의 도시가 있다. 그리고 한 도시에서 출발하여 다른 도시에 도착하는 M개의 버스가 있다. 우리는 A번째 도시에서 B번째 도시까지 가는데 드는 버스 비용을 최소화 시키려고 한다. A번째 도시에서 B번째 도시까지 가는데 드는 최소비용을 출력하여라. 도시의 번호는 1부터 N까지이다.

입력

첫째 줄에 도시의 개수 N(1 ≤ N ≤ 1,000)이 주어지고 둘째 줄에는 버스의 개수 M(1 ≤ M ≤ 100,000)이 주어진다. 그리고 셋째 줄부터 M+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그 버스의 출발 도시의 번호가 주어진다. 그리고 그 다음에는 도착지의 도시 번호가 주어지고 또 그 버스 비용이 주어진다. 버스 비용은 0보다 크거나 같고, 100,000보다 작은 정수이다.

그리고 M+3째 줄에는 우리가 구하고자 하는 구간 출발점의 도시번호와 도착점의 도시번호가 주어진다. 출발점에서 도착점을 갈 수 있는 경우만 입력으로 주어진다.

출력

첫째 줄에 출발 도시에서 도착 도시까지 가는데 드는 최소 비용을 출력한다.

내가 푼 풀이

 

접근방법: 다익스트라 알고리즘

1. 주어진 버스들로 2차원 배열을 만든다 (arr[i][j]: i도시에서 j도시까지의 거리)

1-1. 출발지와 도착지가 동일한경우 더 작은값을 넣는다.

2. 최단거리를 저장할 배열을 만든다.

3. 다익스트라 탐색을 이용해 이동가능한 버스를 통해 최단거리를 갱신한다.

4. 최종 갱신된 최단거리 배열에서 도착점의 원소 출력

 

import Foundation

// 입력받기
let N = Int(readLine()!)!
let M = Int(readLine()!)!
var cities = Array(repeating: Array(repeating: Int.max, count: N+1), count: N+1)
var bus = [[Int]]()
for i in 0..<M {
    let b = readLine()!.split(separator: " ").map{Int(String($0))!}
    bus.append(b)
}
let direct = readLine()!.split(separator: " ").map{Int(String($0))!}

// 2차원 배열로 만들기 cities[i][j] : i도시에서 j도시로 가는 거리
// 출발지와 목적지가 동일한 버스가 주어진다면 값비교해서 최솟값만 넣기
for i in bus {
    if cities[i[0]][i[1]] == Int.max {
        cities[i[0]][i[1]] = i[2]
    } else {
        if cities[i[0]][i[1]] > i[2] {
            cities[i[0]][i[1]] = i[2]
        }
    }
    
}
// 최단경로를 저장해놓을 배열
var md = Array(repeating: Int.max, count: N+1)
md[direct[0]] = 0
var queue = [(cur: direct[0], cost: 0)]

// 탐색
while !queue.isEmpty {
    let node = queue.removeFirst()
    for i in 1...N {
        // 현재 도시와 인접도시가 버스를 통해 갈 수 있고, 저장된 최단거리보다 더 짧다면 갱신해주고, 큐에 삽입
        if cities[node.cur][i] != Int.max && md[node.cur] + cities[node.cur][i] < md[i] {
            md[i] = md[node.cur] + cities[node.cur][i]
            queue.append((cur: i, cost: md[i]))
        }
    }
}
print(md[direct[1]])

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

BOJ-4485 녹색 옷 입은 애가 젤다지? Swift  (0) 2024.04.24
BOJ-18352 특정 거리의 도시 찾기 Swift  (0) 2024.04.24
BOJ-1027 고층 건물 Swift  (0) 2024.04.23
BOJ-3190 뱀 Swift  (0) 2024.04.22
BOJ-1024 수열의 합 Swift  (1) 2024.04.20

+ Recent posts