문제

방향 없는 그래프가 주어졌을 때, 연결 요소 (Connected Component)의 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 정점의 개수 N과 간선의 개수 M이 주어진다. (1 ≤ N ≤ 1,000, 0 ≤ M ≤ N×(N-1)/2) 둘째 줄부터 M개의 줄에 간선의 양 끝점 u와 v가 주어진다. (1 ≤ u, v ≤ N, u ≠ v) 같은 간선은 한 번만 주어진다.

출력

첫째 줄에 연결 요소의 개수를 출력한다.

내가 푼 풀이

- 연결요소는 점이 주어졌을 때, 연결된 점의 무리들의 총개수를 말한다.

- 한 점이 어떤 점도 연결되어있지 않아도, 연결요소에 포함된다.

- 해당 점과 연결리스트를 작성하고, 순서는 상관없으므로 BFS , DFS 아무거나 사용해서 연결된 점들을 모두 탐색하면 연결요소 + 1 이 되는 형식으로 짰다.

 

import Foundation

var input = readLine()!.split(separator: " ").map{ Int($0)! }
var graph = [Int: [Int]]()
var start = [Int]()
var result = 0
var visitedQueue = [Int]()
var needVisitQueue = [Int]()

// 연결리스트 작성
for i in 0..<input[1] {
    var inp = readLine()!.split(separator: " ").map{ Int($0)! }
    
    if graph[inp[0]] == nil {
        graph[inp[0]] = [inp[1]]
    } else {
        graph[inp[0]]!.append(inp[1])
    }
    if graph[inp[1]] == nil {
        graph[inp[1]] = [inp[0]]
    } else {
        graph[inp[1]]!.append(inp[0])
    }
    
    if !start.contains(inp[0]) {
        start.append(inp[0])
    }
    if !start.contains(inp[1]) {
        start.append(inp[1])
    }
}

// 연결되어 있지 않은 점도 본인의 점으로 연결
for i in 1...input[0] {
    if graph[i] == nil {
        graph[i] = [i]
        start.append(i)
    }
    
}

// 주어진 점 모두 탐색할때까지 DFS
while visitedQueue.count != start.count {
    var node = 0
    for i in 0..<start.count {
        if !visitedQueue.contains(start[i]) {
            node = start[i]
            break
        }
    }
    needVisitQueue.append(node)
    result += 1
    
    while !needVisitQueue.isEmpty {
        var n = needVisitQueue.removeLast()
        if visitedQueue.contains(n) { continue }
        visitedQueue.append(n)
        
        needVisitQueue += graph[n]!
    }
}

print(result)

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

BOJ-9655 돌 게임 Swift  (0) 2023.06.01
BOJ-9184 신나는 함수 실행 Swift  (0) 2023.06.01
BOJ-12904 A와B Swift  (0) 2023.05.31
BOJ-2225 합분해 Swift  (0) 2023.05.31
BOJ-1697 숨바꼭질 Swift  (0) 2023.05.30

문제

수빈이는 A와 B로만 이루어진 영어 단어가 존재한다는 사실에 놀랐다. 대표적인 예로 AB (Abdominal의 약자), BAA (양의 울음 소리), AA (용암의 종류), ABBA (스웨덴 팝 그룹)이 있다.

이런 사실에 놀란 수빈이는 간단한 게임을 만들기로 했다. 두 문자열 S와 T가 주어졌을 때, S를 T로 바꾸는 게임이다. 문자열을 바꿀 때는 다음과 같은 두 가지 연산만 가능하다.

  • 문자열의 뒤에 A를 추가한다.
  • 문자열을 뒤집고 뒤에 B를 추가한다.

주어진 조건을 이용해서 S를 T로 만들 수 있는지 없는지 알아내는 프로그램을 작성하시오. 

입력

첫째 줄에 S가 둘째 줄에 T가 주어진다. (1 ≤ S의 길이 ≤ 999, 2 ≤ T의 길이 ≤ 1000, S의 길이 < T의 길이)

출력

S를 T로 바꿀 수 있으면 1을 없으면 0을 출력한다.

내가 푼 풀이

- S를 T로 만들기에 여러가지 경우의 수가 존재할꺼라 생각한다.

- 주어진 T를 역으로 연산해서 S가 되는지 판단했다.

- 두가지 연산결과는 A혹은 B가 뒤에 붙으므로 연산결과는 항상 다를것이다.

- 맨 뒤 문자가 A면 1번연산, B면 2번연산으로 역으로 제거하였다.

 

import Foundation

var s1 = Array(readLine()!).map{ String($0) }
var s2 = Array(readLine()!).map{ String($0) }

// S 와 T가 같아질때까지
// 맨 뒤 문자에 따라 연산을 달리한다.
while s2.count != s1.count {
    var lastStr = s2.removeLast()
    if lastStr == "B" {
        s2 = s2.reversed()
    }
}
if s1 == s2 {
    print(1)
} else {
    print(0)
}

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

BOJ-9184 신나는 함수 실행 Swift  (0) 2023.06.01
BOJ-11724 연결 요소의 개수 Swift  (0) 2023.05.31
BOJ-2225 합분해 Swift  (0) 2023.05.31
BOJ-1697 숨바꼭질 Swift  (0) 2023.05.30
BOJ-7576 토마토 Swift  (0) 2023.05.30

문제

0부터 N까지의 정수 K개를 더해서 그 합이 N이 되는 경우의 수를 구하는 프로그램을 작성하시오.

덧셈의 순서가 바뀐 경우는 다른 경우로 센다(1+2와 2+1은 서로 다른 경우). 또한 한 개의 수를 여러 번 쓸 수도 있다.

입력

첫째 줄에 두 정수 N(1 ≤ N ≤ 200), K(1 ≤ K ≤ 200)가 주어진다.

출력

첫째 줄에 답을 1,000,000,000으로 나눈 나머지를 출력한다.

내가 푼 풀이

- N이 주어졌을때, 0부터 N까지 k번을 더해서 N을 만드는 경우의 수를 구한다.

- K = 1 인경우, 한번만 더해서 N을 만드려면 N 자기 자신을 더하는 방법 1가지다.

- K = 2 인경우, 두번 더해서 N을 만드려면 , 0 + N , 1 + (N-1), ... , N + 0 으로 N가지이다.

- 이를 "dp[n][k] = 0부터N까지의 정수 K번 더해서 합이 N이되는 경우의수" 로 정의하자.

- K = 1 인경우 자기 자신만 더하면 되므로 dp[n][1] = 1

- K = 2 인경우 자기 자신을 1씩차감한만큼 더해주면 되므로, dp[n][2] = n 이된다.

- 이제 K = 3 부터는 이전의 값을 저장된 값을 사용하려한다.

- 3번 더해서 N을 만드는 경우의 수를 초기값이 0부터 N까지의 경우의수를 생각해보자.

예시로 N = 3 , K = 3 일때

  • 초기값 = 0 이라면, 나머지 3, 두번을 더해서 3을 만들때 => dp[3][2]
  • 초기값 = 1 이라면, 나머지 2, 두번을 더해서 3을만들때 => dp[2][2]
  • 초기값 = 2 이라면, 나머지 1, 두번을 더해서 3을 만들때 => dp[1][2]
  • 초기값 = 3 이라면 나머지 0, 두번을 더해서 3을 만들때 => dp[0][2]

로 나타낼 수 있다.

이는 dp[n][k] = k번 더해서 합이 n이되는 경우의 수 이기때문이다.

위를 바탕으로 N = 5, K = 3 일때의 dp를 구하면 다음과 같다.

DP 1 2 3
0 1 1 1
1 1 2 3
2 1 3 6
3 1 4 10
4 1 5 15
5 1 6 21

따라서 0부터5까지 정수를 3번 더해서 합이 5가되는 경우의 수는 21이 된다.dp[5][3]

 

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

 

import Foundation

var input = readLine()!.split(separator: " ").map{ Int($0)! }
var dp = Array(repeating: Array(repeating: 0, count: input[1]+1), count: input[0]+1)

// DP 입력
// dp[n][k] = dp[n][k] + dp[i][k-1] (i는 0부터 n까지의 정수)
for i in 1...input[1] {
    for j in 0...input[0] {
        if i == 1 || j == 0 {
            dp[j][i] = 1
            continue
        }
        for k in 0...j {
            dp[j][i] = (dp[j][i] + dp[k][i-1]) % 1000000000
        }
    }
}
print(dp[input[0]][input[1]])

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

BOJ-11724 연결 요소의 개수 Swift  (0) 2023.05.31
BOJ-12904 A와B Swift  (0) 2023.05.31
BOJ-1697 숨바꼭질 Swift  (0) 2023.05.30
BOJ-7576 토마토 Swift  (0) 2023.05.30
BOJ-15903 카드 합체 놀이 Swift  (0) 2023.05.30

문제

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일 때 걷는다면 1초 후에 X-1 또는 X+1로 이동하게 된다. 순간이동을 하는 경우에는 1초 후에 2*X의 위치로 이동하게 된다.

수빈이와 동생의 위치가 주어졌을 때, 수빈이가 동생을 찾을 수 있는 가장 빠른 시간이 몇 초 후인지 구하는 프로그램을 작성하시오.

입력

첫 번째 줄에 수빈이가 있는 위치 N과 동생이 있는 위치 K가 주어진다. N과 K는 정수이다.

출력

수빈이가 동생을 찾는 가장 빠른 시간을 출력한다.

내가 푼 풀이

- BFS로 인접한 접점을 탐색해서 걸린 시간을 출력한다.

- 예제로 주어진 X = 5, K = 17 일때 다음과 같다.

 

- 이미 방문했던 지점이라면 스킵하고, 방문하지 않은 지점이라면 걸린시간을 더해준다.

- 걸린시간을 계산하기위해 dp[] 의 크기를 100,000만큼 선언해준다. ( 이 문제의 최대지점이 100,000 이기때문에)

- BFS 방식으로 인접한 지점 우선으로 탐색하고, 방문하지 않은 지점이라면 dp[방문할지점] = dp[이전 지점] + 1 을 해준다.

- 위에선 지점 5에서 4로갈때 1초가 들고, 지점 4에서 3으로 갈때, dp[3] = dp[4] + 1 이 된다.

- 이미 방문한 지점을 배열로 선언하여 contains 함수를 이용해 방문 했는지 확인하려 했으나, 시간초과가 떴다.

- BFS, DFS 중 어떤방식을 써야하는지는 알지만, 해당 알고리즘을 코드로 구현할때, 시간초과가 걸리지않게 따로 저장하는 방식을 쓰자

 

import Foundation

var input = readLine()!.split(separator: " ").map{ Int($0)! }
var needVisitedQueue = [input[0]]
var idx = 0
var arr = [Int]()
var dp = Array(repeating: 0, count: 100001)
var visited = Array(repeating: 0, count: 100001)
visited[input[0]] = 1
// 소요시간을 저장할 배열 DP
// 해당 지점을 방문했는지 확인하기위한 배열 visited (visited[n] == 1 이면 지점 N은 이미 방문했다.)
// BFS로 시작점에서 인접한 노드순으로 탐색한다.
while idx < needVisitedQueue.count {
    var node = needVisitedQueue[idx]
    idx += 1
    if node == input[1] { break }
    
    // 해당 노드의 인접노드들이 범위안에 있고, 방문하지 않은곳이라면 다음방문 순서로 둔다.
    if node-1 >= 0 && visited[node-1] != 1 {
        needVisitedQueue.append(node-1)
        visited[node-1] = 1
        dp[node-1] = dp[node] + 1
    }
    if node+1 <= 100000 && visited[node+1] != 1 {
        needVisitedQueue.append(node+1)
        visited[node+1] = 1
        dp[node+1] = dp[node] + 1
    }
    if node*2 <= 100000 && visited[node*2] != 1 {
        needVisitedQueue.append(node*2)
        visited[node*2] = 1
        dp[node*2] = dp[node] + 1
    }
}

print(dp[input[1]])

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

BOJ-12904 A와B Swift  (0) 2023.05.31
BOJ-2225 합분해 Swift  (0) 2023.05.31
BOJ-7576 토마토 Swift  (0) 2023.05.30
BOJ-15903 카드 합체 놀이 Swift  (0) 2023.05.30
BOJ-10775 공항 Swift  (1) 2023.05.30

문제

철수의 토마토 농장에서는 토마토를 보관하는 큰 창고를 가지고 있다. 토마토는 아래의 그림과 같이 격자 모양 상자의 칸에 하나씩 넣어서 창고에 보관한다. 

창고에 보관되는 토마토들 중에는 잘 익은 것도 있지만, 아직 익지 않은 토마토들도 있을 수 있다. 보관 후 하루가 지나면, 익은 토마토들의 인접한 곳에 있는 익지 않은 토마토들은 익은 토마토의 영향을 받아 익게 된다. 하나의 토마토의 인접한 곳은 왼쪽, 오른쪽, 앞, 뒤 네 방향에 있는 토마토를 의미한다. 대각선 방향에 있는 토마토들에게는 영향을 주지 못하며, 토마토가 혼자 저절로 익는 경우는 없다고 가정한다. 철수는 창고에 보관된 토마토들이 며칠이 지나면 다 익게 되는지, 그 최소 일수를 알고 싶어 한다.

토마토를 창고에 보관하는 격자모양의 상자들의 크기와 익은 토마토들과 익지 않은 토마토들의 정보가 주어졌을 때, 며칠이 지나면 토마토들이 모두 익는지, 그 최소 일수를 구하는 프로그램을 작성하라. 단, 상자의 일부 칸에는 토마토가 들어있지 않을 수도 있다.

입력

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토들의 정보가 주어진다. 즉, 둘째 줄부터 N개의 줄에는 상자에 담긴 토마토의 정보가 주어진다. 하나의 줄에는 상자 가로줄에 들어있는 토마토의 상태가 M개의 정수로 주어진다. 정수 1은 익은 토마토, 정수 0은 익지 않은 토마토, 정수 -1은 토마토가 들어있지 않은 칸을 나타낸다.

토마토가 하나 이상 있는 경우만 입력으로 주어진다.

출력

여러분은 토마토가 모두 익을 때까지의 최소 날짜를 출력해야 한다. 만약, 저장될 때부터 모든 토마토가 익어있는 상태이면 0을 출력해야 하고, 토마토가 모두 익지는 못하는 상황이면 -1을 출력해야 한다.

내가 푼 풀이

- 처음에는 토마토가 들어있지 않은 칸을 제외한 칸끼리의 연결리스트를 작성해, BFS를 실행 시켰는데 시간초과가 떴다.

- 위 같은 문제는 연결방향이 정해져있고, 범위도 정해져있기에 따로 연결리스트까진 필요없지만, 탐색을 시작할 점은 필요하다.

- 상자의 배열을 입력받아, 토마토가 들어있다면 탐색시작 인덱스로 저장해둔다.

- 시작 인덱스부터 상하좌우 방향으로 상자범위안에 있고, 익지않은 토마토가 있다면 해당토마토를 익힌다.

- 그리고 해당 칸의 경과일을 저장할 day 배열을 상자크기만큼 선언하고, 익지않은 토마토를 발견하면 인접한 익은 토마토의 day 원소 + 1 을 해준다.

- 마지막으로 토마토가 익은 위치를 저장해두고, 상자를 다시 확인했을때, 익지않은 토마토가 없다면, 마지막으로 익은 위치의 day 원소를 출력

- 익지 않은 토마토가 있다면, -1 출력

 

해당 조건대로 코드를 구현해 보자.

 

import Foundation

var input = readLine()!.split(separator: " ").map{ Int($0)! }
var arr = [[Int]]()
var days = Array(repeating: Array(repeating: 0, count: input[0]), count: input[1])
var needVisitQueue = [[Int]]()
var idx = 0
var endDay = [0,0]
var complete = true

// 상자 배열 입력
// 익은 토마토를 발견할 경우 해당 x,y 인덱스를 시작점 배열에 추가
for i in 0..<input[1] {
    arr.append(readLine()!.split(separator: " ").map{ Int($0)! })
    for j in 0..<arr[i].count {
        if arr[i][j] == 1 {
            needVisitQueue.append([j,i])
        }
    }
}
// 상하좌우 방향
var dx = [0, 1, 0, -1]
var dy = [1, 0, -1, 0]

// 탐색시작점부터 모든 인접한 위치 탐색
while idx < needVisitQueue.count {
    var node = needVisitQueue[idx]
    idx += 1
   
    for i in 0..<4 {
        let nx = node[0] + dx[i]
        let ny = node[1] + dy[i]

        if (nx >= 0 && nx < input[0]) && (ny >= 0 && ny < input[1]) {
            if arr[ny][nx] == 0 {
                arr[ny][nx] = 1
                needVisitQueue.append([nx,ny])
                days[ny][nx] = days[node[1]][node[0]] + 1
                endDay = [nx,ny]
            }
        }
    }
}

// 익지않은 토마토를 발견하면, 모두 익지 못하는 상황이다.
for i in 0..<arr.count {
    for j in 0..<arr[0].count {
        if arr[i][j] == 0 {
            complete = false
        }
    }
}

if complete {
    print(days[endDay[1]][endDay[0]])
} else {
    print(-1)
}

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

BOJ-2225 합분해 Swift  (0) 2023.05.31
BOJ-1697 숨바꼭질 Swift  (0) 2023.05.30
BOJ-15903 카드 합체 놀이 Swift  (0) 2023.05.30
BOJ-10775 공항 Swift  (1) 2023.05.30
BOJ-2294 동전 2 Swift  (0) 2023.05.30

문제

석환이는 아기다. 아기 석환이는 자연수가 쓰여져있는 카드를 갖고 다양한 놀이를 하며 노는 것을 좋아한다. 오늘 아기 석환이는 무슨 놀이를 하고 있을까? 바로 카드 합체 놀이이다!

아기 석환이는 자연수가 쓰여진 카드를 n장 갖고 있다. 처음에 i번 카드엔 ai가 쓰여있다. 카드 합체 놀이는 이 카드들을 합체하며 노는 놀이이다. 카드 합체는 다음과 같은 과정으로 이루어진다.

  1. x번 카드와 y번 카드를 골라 그 두 장에 쓰여진 수를 더한 값을 계산한다. (x ≠ y)
  2. 계산한 값을 x번 카드와 y번 카드 두 장 모두에 덮어 쓴다.

이 카드 합체를 총 m번 하면 놀이가 끝난다. m번의 합체를 모두 끝낸 뒤, n장의 카드에 쓰여있는 수를 모두 더한 값이 이 놀이의 점수가 된다. 이 점수를 가장 작게 만드는 것이 놀이의 목표이다.

아기 석환이는 수학을 좋아하긴 하지만, 아직 아기이기 때문에 점수를 얼마나 작게 만들 수 있는지를 알 수는 없었다(어른 석환이는 당연히 쉽게 알 수 있다). 그래서 문제 해결 능력이 뛰어난 여러분에게 도움을 요청했다. 만들 수 있는 가장 작은 점수를 계산하는 프로그램을 만들어보자.

입력

첫 번째 줄에 카드의 개수를 나타내는 수 n(2 ≤ n ≤ 1,000)과 카드 합체를 몇 번 하는지를 나타내는 수 m(0 ≤ m ≤ 15×n)이 주어진다.

두 번째 줄에 맨 처음 카드의 상태를 나타내는 n개의 자연수 a1, a2, …, an이 공백으로 구분되어 주어진다. (1 ≤ ai ≤ 1,000,000)

출력

첫 번째 줄에 만들 수 있는 가장 작은 점수를 출력한다.

내가 푼 풀이

- 카드 합체는 서로다른 x y의 x번째 카드와 y번째 카드를 더한 후 해당카드들에 결과를 덮어 씌우는것이다.

- 카드 합체가 끝나고 모든 카드의 합을 구하는 것인데, 해당 값이 최솟값이 되어야한다.

- 이는 카드합체를 할때, 가장 작은 두 수끼리 합체하면 모든 카드의 합이 결국 최솟값이 된다.

- 주어진 카드를 오름차순으로 정렬 후, 0번째 원소와 1번째 원소를 합체하고, 결과를 다시 오름차순으로 정렬한다.

- 이를 반복해서 카드들의 합을 구하면 최솟값이 된다.

 

import Foundation

var input = readLine()!.split(separator: " ").map{ Int($0)! }
var arr = readLine()!.split(separator: " ").map{ Int($0)! }
arr.sort{$0 < $1}

// 카드 합체
// 카드는 오름차순으로 정렬되어있기에, 0번째 원소와 1번째 원소가 가장 작은 수 2개이다.
for i in 0..<input[1] {
    var sum = arr[0] + arr[1]
    arr[0] = sum
    arr[1] = sum
    arr = arr.sorted{ $0 < $1}
}

print(arr.reduce(0, +))

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

BOJ-1697 숨바꼭질 Swift  (0) 2023.05.30
BOJ-7576 토마토 Swift  (0) 2023.05.30
BOJ-10775 공항 Swift  (1) 2023.05.30
BOJ-2294 동전 2 Swift  (0) 2023.05.30
BOJ-2212 센서 Swift  (0) 2023.05.30

문제

오늘은 신승원의 생일이다.

박승원은 생일을 맞아 신승원에게 인천국제공항을 선물로 줬다.

공항에는 G개의 게이트가 있으며 각각은 1에서 G까지의 번호를 가지고 있다.

공항에는 P개의 비행기가 순서대로 도착할 예정이며, 당신은 i번째 비행기를 1번부터 gi (1 ≤ gi ≤ G) 번째 게이트중 하나에 영구적으로 도킹하려 한다. 비행기가 어느 게이트에도 도킹할 수 없다면 공항이 폐쇄되고, 이후 어떤 비행기도 도착할 수 없다.

신승원은 가장 많은 비행기를 공항에 도킹시켜서 박승원을 행복하게 하고 싶어한다. 승원이는 비행기를 최대 몇 대 도킹시킬 수 있는가?

입력

첫 번째 줄에는 게이트의 수 G (1 ≤ G ≤ 105)가 주어진다.

두 번째 줄에는 비행기의 수 P (1 ≤ P ≤ 105)가 주어진다.

이후 P개의 줄에 gi (1 ≤ gi ≤ G) 가 주어진다.

출력

승원이가 도킹시킬 수 있는 최대의 비행기 수를 출력한다.

내가 푼 풀이

- 게이트에 최대로 도킹할 수 있는 비행기의 수를 구한다.

- P개의 줄에 숫자 N이 주어지면, 해당 비행기는 1부터 N까지의 게이트에 도킹할 수 있다.

- 이를 생각해보면 1은 1부터 1까지 1개, 5는 1부터 5까지 5개의 게이트 중 하나를 도킹할 수 있다는 의미다.

- 최대한 많은 비행기를 도킹시키려면 주어진 값의 최댓값부터 도킹을 해야 비행기를 최대로 도킹할 수 있다.

- 이를 위해 숫자 N이 주어지면 N부터 1까지 도킹되어 있지않으면 도킹하는 방법을 이용했지만 , 시간초과가 떴다.

- 이 문제는 Union-Find 알고리즘을 이용해서 풀어야한다.

 

Union-find를 이용하며 예제를 풀어본다.

게이트 수 G = 4, 비행기 수 P = 6, 도착하는 비행기 배열 [2, 2, 3, 3, 4, 4]

게이트를 배열로 표현한다.

처음에 각 게이트들은 자신의 게이트가 비어있으므로, 자신의 게이트를 가리킨다.

0 1 2 3 4
0 1 2 3 4

먼저 Gi가 2인 비행기가 도착하면, 해당 비행기는 1부터 2까지 최댓값인 곳에 도킹한다.

게이트 2가 현재 비어있으므로, 게이트 2에 도킹한다.

게이트 2에 도킹을 한다면, 게이트 2는 도킹할 수 없으므로 게이트 1을 가리킨다.

이는 Union-find에서 find(2)-1 , find(2)를 union 하는 것이다.

0 1 2 3 4
0 1 1 3 4

다음에 Gi가 2인 비행기가 도착한다.

2번 게이트에 도킹하려 했지만, 이미 2번게이트에 도킹이 되어있다.

2번 게이트는 1번을 가리키므로, 1번 게이트에 도킹을 한다.

이때 find(1) - 1과 find(1)을 union 한다. (0, 1)

0 1 2 3 4
0 0 0 3 4

위 배열로 보았을 때, Gi가 2나 1인 비행기가 오면 더 이상 도킹할 수 없는 상황이다.

다음 비행기의 Gi는 3이다.

3번 게이트에 도킹을 하고, 2번과 union을 해준다.

0 1 2 3 4
0 0 0 0 4

그다음 비행기의 Gi값이 3이지만, 3번 게이트부터 1번 게이트까지 모두 자리가 없으므로, 종료한다.

 

이 문제는 이해하기 쉽지만, 결국 Union-find 알고리즘을 알고 있는지 물어보는 문제인듯하다..

 

위 알고리즘을 통해 코드로 구현해 보자.

 

import Foundation

var g = Int(readLine()!)!
var p = Int(readLine()!)!
var arr = Array(1...g)
arr.insert(0, at: 0)
var result = 0

// Union-find 의 find
func find(_ x: Int) -> Int {
    if arr[x] == x { return x}
    arr[x] = find(arr[x])
    return arr[x]
}
// Union-find 의 union
func union(x: Int, y: Int) {
    arr[find(y)] = find(x)
}

for i in 0..<p {
    var num = Int(readLine()!)!
    // 해당 비행기가 도킹할 수 있는지?
    // 도킹했다면 union을 통해 배열 최신화
    if find(num) == 0 { break }
    result += 1
    union(x: find(num) - 1, y: find(num))
}
print(result)

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

BOJ-7576 토마토 Swift  (0) 2023.05.30
BOJ-15903 카드 합체 놀이 Swift  (0) 2023.05.30
BOJ-2294 동전 2 Swift  (0) 2023.05.30
BOJ-2212 센서 Swift  (0) 2023.05.30
BOJ-2133 타일 채우기 Swift  (0) 2023.05.30

문제

n가지 종류의 동전이 있다. 이 동전들을 적당히 사용해서, 그 가치의 합이 k원이 되도록 하고 싶다. 그러면서 동전의 개수가 최소가 되도록 하려고 한다. 각각의 동전은 몇 개라도 사용할 수 있다.

사용한 동전의 구성이 같은데, 순서만 다른 것은 같은 경우이다.

입력

첫째 줄에 n, k가 주어진다. (1 ≤ n ≤ 100, 1 ≤ k ≤ 10,000) 다음 n개의 줄에는 각각의 동전의 가치가 주어진다. 동전의 가치는 100,000보다 작거나 같은 자연수이다. 가치가 같은 동전이 여러 번 주어질 수도 있다.

출력

첫째 줄에 사용한 동전의 최소 개수를 출력한다. 불가능한 경우에는 -1을 출력한다.

내가 푼 풀이

- k원이 되도록 n가지 동전의 개수가 최소가 되게끔 한다.

- 기본적으로 k원이 될때, n가지 동전 중 가치가 높은 동전을 최대한 많이 사용하면 개수가 최소가 된다.

- dp [n] = n원이 되도록 하는 동전의 최소개수 라 정의하자.

- 예제 입력을 통해 어떻게 코드로 구현했는지 보자.

 

n = 3, k = 15, [1,5,12] : 3가지의 동전으로 15원을 만드는 동전의 최소개수는?

위의  입력이 주어졌을 때, 동전을 한번 사용하는 경우 : dp[1] = 1 , dp[5] = 1, dp[12] = 1 이 된다.

dp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
dp[n] 1 2 3 4 1             1      

- 2원: 1원 + 1원  -> dp[2] = dp[1] + 1

- 3원: 2원 + 1원  -> dp[3] = dp[2] + 1

- 4원: 3원 + 1원  -> dp[4] = dp[3] + 1

- 5원: 4원 + 1원,  5원 dp[5] = min(dp[0] + 1 , dp[4] + 1)

5원을 만들 수 있는 경우의 수 중 최솟값을 저장한다. 

dp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
dp[n] 1 2 3 4 1 2 3 4 5 2 3 1 2 3 3

dp[n] 은 n원을 만드는 최소 동전개수이므로 1원 6개를 만드는 경우는 배제하고, 6원을 만드는 최적의 방법만 검사한다.

- 6원: 5원 + 1원 : dp[6] = dp[5] + 1

- 10원: 5원 2개,  9원 + 1원 : min( dp[5] + 1 , dp[9] + 1 )

 

위와 같이 n원을 만들 때, 사용 가능한 동전들의 경우의 수들 중 최솟값을 저장한다.

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

 

import Foundation

var input = readLine()!.split(separator: " ").map{ Int($0)! }
var arr = [Int]()
var dp = Array(repeating: 0, count: input[1]+1)
dp[0] = 0

// n가지 동전의 종류를 배열로 입력
for i in 0..<input[0] {
    var num = Int(readLine()!)!
    arr.append(num)
}

// DP 입력
// dp[n] = n원을 만드는 최소 동전 개수
for i in 1...input[1] {
    var a = [Int]()
    // 해당 동전을 사용할 수 있는지 확인한다.
    // 주어진 동전들을 이용해서 만들수 있는지 확인: dp[i-arr[j]] != -1
    for j in 0..<arr.count {
        if i - arr[j] >= 0 && dp[i-arr[j]] != -1 {
            a.append(dp[i-arr[j]] + 1)
        }
    }
    // 만들 수 없다면 -1
    // 만들 수 있다면 만드는 경우의 수 중 최솟값을 저장
    if a.isEmpty {
        dp[i] = -1
    } else {
        dp[i] = a.min()!
    }
}
print(dp[input[1]])

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

BOJ-15903 카드 합체 놀이 Swift  (0) 2023.05.30
BOJ-10775 공항 Swift  (1) 2023.05.30
BOJ-2212 센서 Swift  (0) 2023.05.30
BOJ-2133 타일 채우기 Swift  (0) 2023.05.30
BOJ-1520 내리막 길 Swift  (0) 2023.05.29

+ Recent posts