문제

N×N크기의 행렬로 표현되는 종이가 있다. 종이의 각 칸에는 -1, 0, 1 중 하나가 저장되어 있다. 우리는 이 행렬을 다음과 같은 규칙에 따라 적절한 크기로 자르려고 한다.

  1. 만약 종이가 모두 같은 수로 되어 있다면 이 종이를 그대로 사용한다.
  2. (1)이 아닌 경우에는 종이를 같은 크기의 종이 9개로 자르고, 각각의 잘린 종이에 대해서 (1)의 과정을 반복한다.

이와 같이 종이를 잘랐을 때, -1로만 채워진 종이의 개수, 0으로만 채워진 종이의 개수, 1로만 채워진 종이의 개수를 구해내는 프로그램을 작성하시오.

입력

첫째 줄에 N(1 ≤ N ≤ 37, N은 3k 꼴)이 주어진다. 다음 N개의 줄에는 N개의 정수로 행렬이 주어진다.

출력

첫째 줄에 -1로만 채워진 종이의 개수를, 둘째 줄에 0으로만 채워진 종이의 개수를, 셋째 줄에 1로만 채워진 종이의 개수를 출력한다.

내가 푼 풀이

접근방법: 분할정복

1. 주어진 배열이 모두 같은원소로 이루어졌는지 판단

2. 이루어지지 않았다면 해당배열을 같은크기의 배열 9등분으로 나눔

3. 모두 같은원소로 이루어질때 까지 1-2 반복(재귀호출)

4. 같은원소로 이루어졌다면 해당 원소에따라 다르게 저장

5. 저장된 값을 출력

 

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

import Foundation

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

// 분할
func divide(_ xs: Int,_ xe: Int,_ ys: Int,_ ye: Int,_ arr: [[String]],_ N: Int) {
    if !check(xs, xe, ys, ye, arr) {
        let m = N/3
        // 9가지로 분할, 해당 분할 위치에따른 재귀호출
        // [1, 2, 3]
        // [4, 5, 6]
        // [7, 8, 9]
        
        // 1
        divide(xs, xs+m-1, ys, ys+m-1, arr, N/3)
        // 2
        divide(xs+m, xs+m*2-1, ys, ys+m-1, arr, N/3)
        // 3
        divide(xs+m*2, xe, ys, ys+m-1, arr, N/3)
        // 4
        divide(xs, xs+m-1, ys+m, ys+m*2-1, arr, N/3)
        // 5
        divide(xs+m, xs+m*2-1, ys+m, ys+m*2-1, arr, N/3)
        // 6
        divide(xs+m*2, xe, ys+m, ys+m*2-1, arr, N/3)
        // 7
        divide(xs, xs+m-1, ys+m*2, ye, arr, N/3)
        // 8
        divide(xs+m, xs+m*2-1, ys+m*2, ye, arr, N/3)
        // 9
        divide(xs+m*2, xe, ys+m*2, ye, arr, N/3)
    }
}

// 같은 원소로 이루어졌는지 확인
func check(_ xs: Int,_ xe: Int,_ ys: Int,_ ye: Int,_ arr: [[String]]) -> Bool{
    let val = arr[ys][xs]
    for i in ys...ye {
        for j in xs...xe {
            if val != arr[i][j] { return false }
        }
    }
    // 해당 원소의 따라 다르게 저장
    switch val {
    case "-1": result[0] += 1
    case "0": result[1] += 1
    case "1": result[2] += 1
    default:
        break
    }
    return true
}

// 실행 및 출력
divide(1, N, 1, N, arr, N)
print("\(result[0])\n\(result[1])\n\(result[2])\n")

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

BOJ-11401 이항 계수 3 Swift  (0) 2024.05.03
BOJ-1629 곱셈 Swift  (0) 2024.05.03
BOJ-1992 쿼드트리 Swift  (0) 2024.05.03
BOJ-10986 나머지 합 Swift  (1) 2024.05.02
BOJ-11659 구간 합 구하기 4 Swift  (1) 2024.05.01

문제

흑백 영상을 압축하여 표현하는 데이터 구조로 쿼드 트리(Quad Tree)라는 방법이 있다. 흰 점을 나타내는 0과 검은 점을 나타내는 1로만 이루어진 영상(2차원 배열)에서 같은 숫자의 점들이 한 곳에 많이 몰려있으면, 쿼드 트리에서는 이를 압축하여 간단히 표현할 수 있다.

주어진 영상이 모두 0으로만 되어 있으면 압축 결과는 "0"이 되고, 모두 1로만 되어 있으면 압축 결과는 "1"이 된다. 만약 0과 1이 섞여 있으면 전체를 한 번에 나타내지를 못하고, 왼쪽 위, 오른쪽 위, 왼쪽 아래, 오른쪽 아래, 이렇게 4개의 영상으로 나누어 압축하게 되며, 이 4개의 영역을 압축한 결과를 차례대로 괄호 안에 묶어서 표현한다

위 그림에서 왼쪽의 영상은 오른쪽의 배열과 같이 숫자로 주어지며, 이 영상을 쿼드 트리 구조를 이용하여 압축하면 "(0(0011)(0(0111)01)1)"로 표현된다. N ×N 크기의 영상이 주어질 때, 이 영상을 압축한 결과를 출력하는 프로그램을 작성하시오.

입력

첫째 줄에는 영상의 크기를 나타내는 숫자 N 이 주어진다. N 은 언제나 2의 제곱수로 주어지며, 1 ≤ N ≤ 64의 범위를 가진다. 두 번째 줄부터는 길이 N의 문자열이 N개 들어온다. 각 문자열은 0 또는 1의 숫자로 이루어져 있으며, 영상의 각 점들을 나타낸다.

출력

영상을 압축한 결과를 출력한다.

내가 푼 풀이

접근방법: 분할정복

1. 처음에 주어진 배열이 모두 같은원소로 이루어졌는지 확인

2. 같은원소로 이루어지지 않았다면 해당배열을 4등분

3. 재귀호출로 1-2번 반복하다가 같은원소로 이루어졌다면 해당배열의 가장 왼쪽위 원소를 출력

4. 원소를 출력할때는 압축이 되었으므로 괄호 ( )를 감싸서 출력한다.

 

 

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

import Foundation

// 입력
let N = Int(readLine()!)!
var arr = [[Character]]()
arr.append(["0"])
for i in 0..<N {
    var a = Array(readLine()!)
    a.insert("0", at: 0)
    arr.append(a)
}

// 분할
func divide(_ xs: Int,_ xe: Int,_ ys: Int,_ ye: Int,_ arr: [[Character]]) -> String {
    var s = "\(arr[ys][xs])"
    // 같은원소로 이루어지지 않았다면 4등분
    if !check(xs,xe,ys,ye,arr) {
        let xm = (xs+xe)/2
        let ym = (ys+ye)/2
        // 압축하는과정이므로 괄호로 감싼다.
        s = "(\(divide(xs, xm, ys, ym, arr))\(divide(xm+1, xe, ys, ym, arr))\(divide(xs, xm, ym+1, ye, arr))\(divide(xm+1, xe, ym+1, ye, arr)))"
    }
    // 같은 원소로 이루어졌다면 배열의 왼쪽위 원소 출력
    return s
}

// 해당 NxN배열이 모두 같은원소로 이루어졌는지 확인
func check(_ xs: Int,_ xe: Int,_ ys: Int,_ ye: Int,_ arr: [[Character]]) -> Bool {
    let color = arr[ys][xs]
    for i in ys...ye {
        for j in xs...xe {
            if color != arr[i][j] { return false }
        }
    }
    return true
}
print(divide(1, N, 1, N, arr))

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

BOJ-1629 곱셈 Swift  (0) 2024.05.03
BOJ-1780 종이의 개수 Swift  (0) 2024.05.03
BOJ-10986 나머지 합 Swift  (1) 2024.05.02
BOJ-11659 구간 합 구하기 4 Swift  (1) 2024.05.01
14725-개미굴 Swift  (0) 2024.04.30

문제

수 N개 A1, A2, ..., AN이 주어진다. 이때, 연속된 부분 구간의 합이 M으로 나누어 떨어지는 구간의 개수를 구하는 프로그램을 작성하시오.

즉, Ai + ... + Aj (i ≤ j) 의 합이 M으로 나누어 떨어지는 (i, j) 쌍의 개수를 구해야 한다.

입력

첫째 줄에 N과 M이 주어진다. (1 ≤ N ≤ 106, 2 ≤ M ≤ 103)

둘째 줄에 N개의 수 A1, A2, ..., AN이 주어진다. (0 ≤ Ai ≤ 109)

출력

첫째 줄에 연속된 부분 구간의 합이 M으로 나누어 떨어지는 구간의 개수를 출력한다.

내가 푼 풀이

첫번째 접근방법: 세그먼트 트리(시간초과)

구간합트리로 해당 구간합을 구해 나누어 떨어지는지 확인해보았다.

구간합트리의 모든노드는 모든 구간합의 경우의수가 아니였고, 구간합트리를 만들어도 모든 구간합의 경우의수를 구하려니 2중 반복문으로 시간초과가 나게되었다.

 

이 문제는 수학적지식을 이용해서 풀어야만 했었다. 지식의 한계를 느끼고 다른사람의 해석을 보았다.

 

두번째 접근방법: 합배열과 구간합

구간합배열은 이렇게 만들 수 있다.

S(1), S(2), S(3)....

S(1)은 1부터1까지, S(2)는 1부터2까지, S(3)은 1부터3까지의 구간합

  A[1] A[2] A[3] A[4] A[5]
A 1 2 3 1 2
S 1 3 6 7 9
S[i] % M 1 0 0 1 0

 

여기서 구간합배열인 S를 M으로 나누어보면 아래 배열과 같다 [1, 0, 0, 1, 0]

이를 해석해보면 구간합 [1~2], [1~3], [1~5]는 M과 나누어떨어지고, [1~1], [1~4]는 나머지 1이 남는다.

또한 구간합은 S[i~j] = S[j] - S[i-1]로 나타낼 수 있다. 예로 2에서4까지의 구간합은 S[4] - S[1]이다.

이 특징을 이용하면 S[4] - S[1] = 1 - 1 = 0이되고 이는 M으로 나누어떨어진다고 볼 수 있다.

따라서 우리는 아래처럼 종합할 수 있다.

모든 구간합의 경우의수는 아래와 같다.

1. 구간합배열S를 M으로 나누었을때의 0이되는 구간합

2. 나머지배열중 서로 나머지가 같아서 S[i~j] = S[j] - S[i-1] = 0이되는 두개의 나머지배열

 

1번의 경우의수는 S[2], S[3], S[5] 총 3개가 된다.

2번의 경우의 수는 같은 나머지원소중에서 2개를 뽑으면된다(S[j] - S[i-1] = 0을 성립하기 위한 S[j]와 S[i-1]이 필요하다.)

나머지가 0인것중에서 2개를 뽑는 수: ${_3}C{_2}$ -> 3*(3-1)/2 = 3

나머지가 1인것중에서 2개를 뽑는 수: ${_2}C{_2}$  -> 2*(2-1)/2 = 1

3 + 3 + 1 = 7 이 된다.

 

M으로 나누었을때 나머지는 0부터 M-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 S = [Int]()
var sum = 0
// 합배열을 M으로 나눈 나머지배열
var divided = [Int]()
// 나누어떨어지는 갯수
var count = 0

// 합배열 만들기
for i in arr {
    sum += i
    S.append(sum)
}

// 나머지 배열만들기
divided = S.map{$0 % M}
// 나머지 배열중 나머지가 0이면 그 구간합은 나누어떨어지므로 더해준다.
count = divided.filter{$0 == 0}.count

// 나머지배열중 같은 나머지의 갯수중에 2개를 고르는 경우의수를 더한다.
// S[i~j] = S[j] - S[i-1] = 0이 되는 두개의 구간합이 필요하므로
for i in 0..<M {
    let num = divided.filter{$0 == i}.count
    count = count + (num * (num-1))/2
}
print(count)

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

BOJ-1780 종이의 개수 Swift  (0) 2024.05.03
BOJ-1992 쿼드트리 Swift  (0) 2024.05.03
BOJ-11659 구간 합 구하기 4 Swift  (1) 2024.05.01
14725-개미굴 Swift  (0) 2024.04.30
BOJ-1043 거짓말 Swift  (0) 2024.04.29

문제

수 N개가 주어졌을 때, i번째 수부터 j번째 수까지 합을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 수의 개수 N과 합을 구해야 하는 횟수 M이 주어진다. 둘째 줄에는 N개의 수가 주어진다. 수는 1,000보다 작거나 같은 자연수이다. 셋째 줄부터 M개의 줄에는 합을 구해야 하는 구간 i와 j가 주어진다.

출력

총 M개의 줄에 입력으로 주어진 i번째 수부터 j번째 수까지 합을 출력한다.

내가 푼 풀이

접근방법: 세그먼트 트리 구조

세그먼트 트리를 구현하여 구간합을 출력한다.

<세그먼트 트리>

https://jenikeju.tistory.com/290

 

세그먼트 트리 Swift

세그먼트 트리세그먼트 트리는 트리형태의 자료구조로 해당 노드에는 구간합이 저장되어 있다.일반적으로 배열에서 특정 구간의 합을 구해야 한다면 O(N)이 걸린다.세그먼트 트리는 노드마다

jenikeju.tistory.com

 

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))!}
arr.insert(0, at: 0)
var tree = Array(repeating: 0, count: arr.count*4)

// 세그먼트 트리 생성함수
func sgInit(_ start: Int,_ end: Int,_ index: Int) -> Int {
    if start == end {
        tree[index] = arr[start]
        return tree[index]
    }
    let mid = (start+end)/2
    tree[index] = sgInit(start, mid, index*2) + sgInit(mid+1, end, index*2+1)
    return tree[index]
}
// 세그먼트 트리생성
sgInit(1, arr.count-1, 1)

// 세그먼트 트리의 구간합 구하기
func sgSum(_ start: Int,_ end: Int,_ index: Int,_ left: Int,_ right: Int) -> Int {
    if left > end || right < start { return 0 }
    if start >= left && end <= right { return tree[index] }
    let mid = (start+end)/2
    
    return sgSum(start, mid, index*2, left, right) + sgSum(mid+1, end, index*2+1, left, right)
}

// 구간합 출력
for i in 0..<M {
    let range = readLine()!.split(separator: " ").map{Int(String($0))!}
    print(sgSum(1, arr.count-1, 1, range[0], range[1]))
}

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

BOJ-1992 쿼드트리 Swift  (0) 2024.05.03
BOJ-10986 나머지 합 Swift  (1) 2024.05.02
14725-개미굴 Swift  (0) 2024.04.30
BOJ-1043 거짓말 Swift  (0) 2024.04.29
BOJ-1976 여행 가자 Swift  (0) 2024.04.29

문제

개미는(뚠뚠) 오늘도(뚠뚠) 열심히(뚠뚠) 일을 하네.

개미는 아무말도 하지 않지만 땀을 뻘뻘 흘리면서 매일 매일을 살길 위해서 열심히 일을 하네.

한 치 앞도(뚠뚠) 모르는(뚠뚠) 험한 이 세상(뚠뚠) 그렇지만(뚠뚠) 오늘도 행복한 개미들!

우리의 천재 공학자 윤수는 이 개미들이 왜 행복한지 궁금해졌다.

행복의 비결이 개미가 사는 개미굴에 있다고 생각한 윤수는 개미굴의 구조를 알아보기 위해 로봇 개미를 만들었다.

로봇 개미는 센서가 있어 개미굴의 각 층에 먹이가 있는 방을 따라 내려가다 더 이상 내려갈 수 없으면 그 자리에서 움직이지 않고 신호를 보낸다.

이 신호로 로봇 개미는 개미굴 각 층을 따라 내려오면서 알게 된 각 방에 저장된 먹이 정보를 윤수한테 알려줄 수 있다.

로봇 개미 개발을 완료한 윤수는 개미굴 탐사를 앞두고 로봇 개미를 테스트 해보기 위해 위 그림의 개미굴에 로봇 개미를 투입했다. 로봇 개미의 수는 각 개미굴의 저장소를 모두 확인할 수 있을 만큼 넣는다.

다음은 로봇 개미들이 윤수에게 보내준 정보다.

  • KIWI BANANA
  • KIWI APPLE
  • APPLE APPLE
  • APPLE BANANA KIWI

공백을 기준으로 왼쪽부터 순서대로 로봇 개미가 각 층마다 지나온 방에 있는 먹이 이름을 뜻한다.

윤수는 로봇 개미들이 보내준 정보를 바탕으로 다음과 같이 개미굴의 구조를 손으로 그려봤다.

APPLE
--APPLE
--BANANA
----KIWI
KIWI
--APPLE
--BANANA

개미굴의 각 층은 "--" 로 구분을 하였다. 또 같은 층에 여러 개의 방이 있을 때에는 사전 순서가 앞서는 먹이 정보가 먼저 나온다.

우리의 천재 공학자 윤수는 복잡한 개미굴들을 일일이 손으로 그리기 힘들어 우리에게 그려달라고 부탁했다.

한치 앞도 모르는 험한 이세상 그렇지만 오늘도 행복한 개미들!

행복의 비결을 알기 위해 윤수를 도와 개미굴이 어떤 구조인지 확인해보자.

입력

첫 번째 줄은 로봇 개미가 각 층을 따라 내려오면서 알게 된 먹이의 정보 개수 N (1 ≤ N ≤ 1000)개가 주어진다.

두 번째 줄부터 N+1 번째 줄까지, 각 줄의 시작은 로봇 개미 한마리가 보내준 먹이 정보 개수 K (1 ≤ K ≤ 15)가 주어진다.

다음 K개의 입력은 로봇 개미가 왼쪽부터 순서대로 각 층마다 지나온 방에 있는 먹이 정보이며 먹이 이름 길이 t는 1 ≤ t ≤ 15를 만족한다. 먹이 정보는 알파벳 대문자로만 이루어져 있다.

출력

개미굴의 시각화된 구조를 출력하여라.

개미굴의 각 층을 "--" 로 구분하며, 같은 층에 여러개의 방이 있을 때에는 사전 순서가 앞서는 먹이 정보가 먼저 나온다.

최상위 굴을 포함하여 하나의 굴에서 개미굴이 여러개로 나뉠 때 먹이 종류별로 최대 한 번만 나올 수 있다.

내가 푼 풀이

 접근방법: 트라이 자료구조

이전에 트라이 자료구조를 사용할 땐, 문자열의 문자 하나하나 찾아가며 일치하는지 판단했었는데

조금 변형하여, 문자열을 하나하나 판단후, 마지막 문자열까지 도달하여 출력하는 방향으로 바꾸었다.

같은 층에서 다른곳으로 트리처럼 나누어질수 있으므로, 입력순서가 같은 두 문자열은 같은 방으로 취급한다.

위 예시그림이 트리형태로 되어있는데 트라이 자료구조가 바로 저그림과 유사하다.

트라이 구조를 구현하고 문제풀이에 맞게 바꾸어주고 출력

 

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

import Foundation

// 트라이 자료구조
class Tries {
    // 문자열과, 다음 자식노드를 저장할 Dictionary 구현
    var child: [String: Tries] = [:]
    // 해당 정보의 마지막 문자인가 판단하는 변수지만 이 문제 해답을 구하기 위해선 사용하진 않았다.
    var end = false
    
    // 트라이 자료구조에 입력
    // arr: 정보로 얻은 문자열의 배열
    // currentIndex: 현재 인덱스(그림에서의 트리 깊이(depth)), idx: arr의 크기
    func insert(_ arr: [String],_ currentIndex: Int,_ idx: Int) {
        // 주어진 문자열배열을 모두 넣었다면 종료
        if currentIndex == idx {
            self.end = true
            return
        }
        // 해당 문자열이 처음 입력되는거라면 자식노드 만들어서 생성
        // 이미 만들어졌다면 통과
        if child[arr[currentIndex]] == nil {
            child[arr[currentIndex]] = Tries()
        }
        // 만들어진 자식노드를 통해서 그 다음 주어지는 문자열을 넣는다.
        child[arr[currentIndex]]!.insert(arr, currentIndex+1, idx)
    }
    
    // 트라이 구조에서 저장된 모든 노드의 문자열 출력
    func find(_ idx: Int) {
        // 같은 깊이라면 사전순으로 출력하기위해 오름차순
        var arr = Array(self.child.keys).sorted{ $0 < $1 }
        // 깊이를 의미하는 문자
        // 깊은만큼 "--" 추가
        var answer = ""
        for i in 0..<idx {
            answer += "--"
        }
        // 해당 깊이의 모든 문자열을 출력한다.
        // 문자열의 자식노드가 존재하면 재귀호출로 출력
        for i in arr {
            print("\(answer)\(i)")
            child[i]!.find(idx+1)
        }
    }
}

// 생성, 입력
var trie = Tries()
let N = Int(readLine()!)!

// 주어진 정보들을 모두 트라이구조에 넣는다.
for i in 0..<N {
    let info = readLine()!.split(separator: " ").map{String($0)}
    let foods = info[1..<info.count].map{String($0)}
    trie.insert(foods, 0, foods.count)
}
// 출력
trie.find(0)

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

BOJ-10986 나머지 합 Swift  (1) 2024.05.02
BOJ-11659 구간 합 구하기 4 Swift  (1) 2024.05.01
BOJ-1043 거짓말 Swift  (0) 2024.04.29
BOJ-1976 여행 가자 Swift  (0) 2024.04.29
BOJ-2357 최솟값과 최댓값 Swift  (0) 2024.04.29

트라이

트라이는 문자열이 트리형태로 저장된 자료구조이다.

특정 문자열을 탐색할때 특화된 알고리즘으로, 트리는 K진 트리이다. (K는 문자열의 첫번째 문자의 종류)

 

예로 문자열 "apple", "banana", "avocado" 세가지가 있다고 하면 아래와 같은 트리형태로 만들 수 있다.

트리의 최상단노드는 비워두고, 그아래로 k개의 접두사를 갖는 자식노드들을 만든다.

문자열 세개중 중복되지 않는 접두사는 a,b로 최상단노드는 두개의 자식노드를 갖는다.

우리가 문자열을 비교할때, 앞에서부터 차례로 읽어나가듯이, 주어진 문자열을 순차적으로 연결시킨다.

그리고 문자열을 모두 연결시켰다면 마지막노드에 해당노드가 마지막임을 저장해둔다.(Bool 변수를 이용)

 

주어진 문자열에서 특정 문자열을 찾을때, 최상단 노드부터 시작하여 문자열을 검사해나간다.

이때 문자열이 연결되어 있지 않거나, 문자를 모두 찾아갔지만 해당 노드가 끝이아니라면, 해당 문자열은 찾을 수 없다.

 

트라이 자료구조는 특정문자열을 수정하거나 찾는데 O(N)만큼 걸리지만(트리형태의 자료구조에서 문자수만큼 연결하면된다.)

각 노드의 자식노드는 문자열의 최대 경우의수(소문자 26개, 대문자 26개)를 저장하기위해 메모리를 할당해야하므로

메모리가 추가적으로 많이필요하다.

 

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

import Foundation

// 트라이 자료구조
class Trie {
    // 트라이의 자식노드 메모리 할당(여기서는 소문자만 취급한다.)
    // 소문자의 아스키코드로 접근하기위해서 소문자의 갯수만큼 할당
    var child: [Trie?] = Array(repeating: nil, count: 26)
    // 해당 노드가 문자열의 마지막인지?
    var end = false
    
    // 트라이에 문자열을 넣는다. 외부에서 문자열String을 받음
    // 받은 문자열을 한개의 문자들로 나누고 insertCharater함수에 넘겨준다.
    // index는 0: 최상단노드부터 추가해주기 위해서
    func insert(_ s: String) {
        let arr: [Character] = Array(s)
        insertCharacter(arr, 0)
    }
    // 문자 배열과 인덱스를 넘겨받고 실행
    func insertCharacter(_ arr: [Character],_ index: Int) {
        // 해당 문자열을 모두 입력했다면 현재 노드가 마지막임을 설정하고 종료
        if arr.count == index {
            self.end = true
            return
        }
        // 아스키코드로 변환 범위: 0...25 (자식노드배열에 넣기위해)
        let num = toNumber(arr[index])
        
        // 현재 문자열의 아스키코드로 자식노드를 생성해준다.
        // apple을 입력해놓고 avocado를 입력할때, a는 이전에 생성해두었지만, v는 생성해두지 않았기에 생성
        if child[num] == nil {
            child[num] = Trie()
        }
        //남은 문자도 트리에 연결하기 위해 재귀호출
        child[num]?.insertCharacter(arr, index+1)
    }
    // 해당 문자를 아스키코드로 변환하지만 0~25 범위에 존재하게끔 소문자의 첫번째 문자 a의 아스키코드를 뺌
    func toNumber(_ c: Character) -> Int{
        return Int(c.asciiValue! - Character("a").asciiValue!)
    }
    
    // 문자열String을 받으면 해당 문자가 존재하는지 bool값 리턴
    // 문자열String을 역시 문자배열로 변환하여 내부함수 findCharacter를 실행
    func find(_ s: String) -> Bool{
        let arr: [Character] = Array(s)
        return findCharacter(arr,0)
    }
    // 문자열이 존재하는지 파악하기위해 재귀호출
    func findCharacter(_ arr: [Character],_ index: Int) -> Bool {
        // 문자열이 모두 존재하고, 마지막 문자가 끝나는노드라면 true
        // 문자열이 모두 존재했지만, 마지막 문자가 끝나지않는 노드라면 false
        if arr.count == index {
            if self.end { return true }
            return false
        }
        // 해당 문자를 아스키코드로 변환
        let num = toNumber(arr[index])
        // 해당문자의 자식노드가 존재하지않다면 더 찾을 수 없으므로 false
        if child[num] == nil { return false }
        // 그다음 문자도 확인
        return child[num]!.findCharacter(arr, index+1)
    }
}

 

'Swift > 자료구조' 카테고리의 다른 글

세그먼트 트리 Swift  (0) 2024.04.29
분리집합과 Union-Find Swift  (1) 2024.04.28
Swift 덱(Deque) 구현  (0) 2024.04.03
Swift 큐(Queue) 구현  (0) 2024.04.03
Swift 우선순위 큐(Priority Queue) 구현  (0) 2023.05.17

문제

지민이는 파티에 가서 이야기 하는 것을 좋아한다. 파티에 갈 때마다, 지민이는 지민이가 가장 좋아하는 이야기를 한다. 지민이는 그 이야기를 말할 때, 있는 그대로 진실로 말하거나 엄청나게 과장해서 말한다. 당연히 과장해서 이야기하는 것이 훨씬 더 재미있기 때문에, 되도록이면 과장해서 이야기하려고 한다. 하지만, 지민이는 거짓말쟁이로 알려지기는 싫어한다. 문제는 몇몇 사람들은 그 이야기의 진실을 안다는 것이다. 따라서 이런 사람들이 파티에 왔을 때는, 지민이는 진실을 이야기할 수 밖에 없다. 당연히, 어떤 사람이 어떤 파티에서는 진실을 듣고, 또다른 파티에서는 과장된 이야기를 들었을 때도 지민이는 거짓말쟁이로 알려지게 된다. 지민이는 이런 일을 모두 피해야 한다.

사람의 수 N이 주어진다. 그리고 그 이야기의 진실을 아는 사람이 주어진다. 그리고 각 파티에 오는 사람들의 번호가 주어진다. 지민이는 모든 파티에 참가해야 한다. 이때, 지민이가 거짓말쟁이로 알려지지 않으면서, 과장된 이야기를 할 수 있는 파티 개수의 최댓값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 사람의 수 N과 파티의 수 M이 주어진다.

둘째 줄에는 이야기의 진실을 아는 사람의 수와 번호가 주어진다. 진실을 아는 사람의 수가 먼저 주어지고 그 개수만큼 사람들의 번호가 주어진다. 사람들의 번호는 1부터 N까지의 수로 주어진다.

셋째 줄부터 M개의 줄에는 각 파티마다 오는 사람의 수와 번호가 같은 방식으로 주어진다.

N, M은 50 이하의 자연수이고, 진실을 아는 사람의 수는 0 이상 50 이하의 정수, 각 파티마다 오는 사람의 수는 1 이상 50 이하의 정수이다.

출력

첫째 줄에 문제의 정답을 출력한다.

내가 푼 풀이

접근방법: Union-Find

진실을 아는사람의 집합과 모르는사람의 집합을 분리하여, 파티마다 진실을 아는사람이 있다면 거짓말을 할 수 없다.

이를 서로소집합인 분리집합에서 Union-Find 연산을 이용한다.

파티에 진실을 모르는사람과 아는사람이 공존한다면, 진실을 모르는사람이 다음 파티에서는 진실을 안상태라 거짓말쟁이가 된다.

따라서 매 파티마다 진실을 모르는사람과 아는사람이 공존하지 않을때 까지, 진실을 몰랐던사람을 아는사람과 같은 집합에 넣는다(Union)

최종적으로 분리된 집합을 이용하여 파티에서 과장된 이야기 할 수 있는 수를 구하면 된다.

 

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

import Foundation

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

// 분리집합
for i in 1...N {
    parent[i] = i
}

// 분리집합끼리 연결한다.
func union(_ x: Int,_ y: Int) {
    let rootX = findd(x)
    let rootY = findd(y)
    
    parent[rootY] = rootX
}

// 해당 원소의 루트노드를 찾는다.
func findd(_ x: Int) -> Int {
    if parent[x] == x { return x}
    parent[x] = findd(parent[x])
    
    return parent[x]
}

// 진실을 아는사람이 있는경우 루트0으로 연결
if know[0] != 0 {
    for i in 1..<know.count {
        union(0, know[i])
    }
}

// party 수만큼 입력받기
for i in 0..<M {
    party.append(readLine()!.split(separator: " ").map{Int(String($0))!})
}

// 매 파티마다 진실을 아는인원과 모르는인원이 공존하지 않을때 까지 반복한다.
var find = true
while find {
    find = false
    var inLiar = false
    for i in 0..<party.count {
        var previous = -1
        for j in 1..<party[i].count {
            let root = findd(party[i][j])
            // 처음 사람이면 previos 초기화
            if previous == -1 {
                previous = root
                continue
            }
            // 그 다음인원이 진실을 알고, 이전인원이 모르는경우
            if (root != previous && root == 0 ){
                inLiar = true
                break
            }
            // 그 다음인원이 진실을 모르고, 이전인원이 아는 경우
            if (previous == 0 && root != 0 ) {
                inLiar = true
                break
            }
            previous = root
        }
        // 파티안에 진실을 아는사람과 모르는사람이 공존한다면 모두 진실을 아는사람으로 바꾼다.(Union)
        if inLiar {
            find = true
            for j in 1..<party[i].count {
                union(0, party[i][j])
            }
        }
        inLiar = false
    }
}

// 완전히 갱신된 파티에서 거짓말을 할 수 있는지 파악
for i in 0..<party.count {
    var liar = false
    for j in 1..<party[i].count {
        if findd(party[i][j]) == 0 {
            liar = true
            break
        }
    }
    if !liar { count += 1}
}

print(count)

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

BOJ-11659 구간 합 구하기 4 Swift  (1) 2024.05.01
14725-개미굴 Swift  (0) 2024.04.30
BOJ-1976 여행 가자 Swift  (0) 2024.04.29
BOJ-2357 최솟값과 최댓값 Swift  (0) 2024.04.29
BOJ-2042 구간 합 구하기 Swift  (0) 2024.04.29

문제

동혁이는 친구들과 함께 여행을 가려고 한다. 한국에는 도시가 N개 있고 임의의 두 도시 사이에 길이 있을 수도, 없을 수도 있다. 동혁이의 여행 일정이 주어졌을 때, 이 여행 경로가 가능한 것인지 알아보자. 물론 중간에 다른 도시를 경유해서 여행을 할 수도 있다. 예를 들어 도시가 5개 있고, A-B, B-C, A-D, B-D, E-A의 길이 있고, 동혁이의 여행 계획이 E C B C D 라면 E-A-B-C-B-C-B-D라는 여행경로를 통해 목적을 달성할 수 있다.

도시들의 개수와 도시들 간의 연결 여부가 주어져 있고, 동혁이의 여행 계획에 속한 도시들이 순서대로 주어졌을 때 가능한지 여부를 판별하는 프로그램을 작성하시오. 같은 도시를 여러 번 방문하는 것도 가능하다.

입력

첫 줄에 도시의 수 N이 주어진다. N은 200이하이다. 둘째 줄에 여행 계획에 속한 도시들의 수 M이 주어진다. M은 1000이하이다. 다음 N개의 줄에는 N개의 정수가 주어진다. i번째 줄의 j번째 수는 i번 도시와 j번 도시의 연결 정보를 의미한다. 1이면 연결된 것이고 0이면 연결이 되지 않은 것이다. A와 B가 연결되었으면 B와 A도 연결되어 있다. 마지막 줄에는 여행 계획이 주어진다. 도시의 번호는 1부터 N까지 차례대로 매겨져 있다.

출력

첫 줄에 가능하면 YES 불가능하면 NO를 출력한다.

내가 푼 풀이

접근방법: 분리집합 Union-Find

주어진 도시들은 연결정보에 따라 연결시키고, 여행계획에서 도시의 루트노드가 동일한지 판단한다.

연결되어 있다면 루트노드가 동일하지만, 연결되어 있지 않다면 루트노드가 동일하지 않다.

 

import Foundation

// 입력받기
let N = Int(readLine()!)!
let M = Int(readLine()!)!
// 분리집합
var parent = Array(repeating: 0, count: N+1)
var isPossible = true
// 분리집합의 각 원소는 초기에 자신을 루트노드로 갖는다.
for i in 1...N {
    parent[i] = i
}
// 원소의 루트노드를 찾는 함수
func find(_ x: Int) -> Int {
    if parent[x] == x { return x}
    parent[x] = find(parent[x])
    
    return parent[x]
}

// 서로 다른 분리집합을 합치는 함수
func union(_ x: Int,_ y: Int) {
    let rootX = find(x)
    let rootY = find(y)
    
    parent[rootY] = rootX
}
// 주어진 연결정보를 토대로 분리집합을 합친다.
for i in 0..<N {
    let arr = readLine()!.split(separator: " ").map{Int(String($0))!}
    for j in 0..<arr.count {
        if arr[j] == 1 {
            union(i+1, j+1)
        }
    }
}

let a = readLine()!.split(separator: " ").map{Int(String($0))!}
var root = -1
// 해당 도시가 연결되어있지않다면, 서로 분리된 분리집합이라면 여행계획이 불가능하다.
for i in 0..<a.count {
    if i == 0 { root = find(a[i])}
    if root != find(a[i]) {
        isPossible = false
        break
    }
}
print(isPossible ? "YES" : "NO")

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

14725-개미굴 Swift  (0) 2024.04.30
BOJ-1043 거짓말 Swift  (0) 2024.04.29
BOJ-2357 최솟값과 최댓값 Swift  (0) 2024.04.29
BOJ-2042 구간 합 구하기 Swift  (0) 2024.04.29
BOJ-1717 집합의 표현 Swift  (0) 2024.04.28

+ Recent posts