문제

바로 어제 최백준 조교가 방 열쇠를 주머니에 넣은 채 깜빡하고 서울로 가 버리는 황당한 상황에 직면한 조교들은, 702호에 새로운 보안 시스템을 설치하기로 하였다. 이 보안 시스템은 열쇠가 아닌 암호로 동작하게 되어 있는 시스템이다.

암호는 서로 다른 L개의 알파벳 소문자들로 구성되며 최소 한 개의 모음(a, e, i, o, u)과 최소 두 개의 자음으로 구성되어 있다고 알려져 있다. 또한 정렬된 문자열을 선호하는 조교들의 성향으로 미루어 보아 암호를 이루는 알파벳이 암호에서 증가하는 순서로 배열되었을 것이라고 추측된다. 즉, abc는 가능성이 있는 암호이지만 bac는 그렇지 않다.

새 보안 시스템에서 조교들이 암호로 사용했을 법한 문자의 종류는 C가지가 있다고 한다. 이 알파벳을 입수한 민식, 영식 형제는 조교들의 방에 침투하기 위해 암호를 추측해 보려고 한다. C개의 문자들이 모두 주어졌을 때, 가능성 있는 암호들을 모두 구하는 프로그램을 작성하시오.

입력

첫째 줄에 두 정수 L, C가 주어진다. (3 ≤ L ≤ C ≤ 15) 다음 줄에는 C개의 문자들이 공백으로 구분되어 주어진다. 주어지는 문자들은 알파벳 소문자이며, 중복되는 것은 없다.

출력

각 줄에 하나씩, 사전식으로 가능성 있는 암호를 모두 출력한다.

내가 푼 풀이

- 주어진 문자들로 L길이의 암호를 만든 뒤 암호의 조건을 따졌다.

- 암호를 만들때 문자를 조합으로 뽑았다.

- 결과값이 중복되지않게 Set을 이용했다.

 

import Foundation

// 입력받기
let input = readLine()!.split(separator: " ").map{Int(String($0))!}
let L = input[0], C = input[1]
let alphabet = readLine()!.split(separator: " ").map{String($0)}
let aeiou = ["a", "e", "i", "o", "u"]
var result = Set<String>()

// 조합
func combi(_ targetArr: [String],_ targetNum: Int,_ index: Int,_ arr: [String]) {
    // L길이의 암호를 뽑았다면 조건 확인
    if arr.count == targetNum {
        if check(arr) {
            result.insert((arr.sorted(by: <).joined()))
        }
        return
    }
    for i in index..<targetArr.count {
        combi(targetArr, targetNum, i+1, arr + [targetArr[i]])
    }
}

// 조건 확인 함수
func check(_ arr: [String]) -> Bool {
    var minimumAE = 0
    var minimumBC = 0
    for i in 0..<arr.count {
        if aeiou.contains(arr[i]) {
            minimumAE += 1
        } else {
            minimumBC += 1
        }
    }
    if minimumAE >= 1 && minimumBC >= 2 {
        return true
    } else {
        return false
    }
}
combi(alphabet, L, 0, [])

for i in result.sorted(by: <) {
    print(i)
}

 

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

BOJ-1476 날짜 계산 Swift  (0) 2024.04.11
BOJ-14500 테트로미노 Swift (Brute-force)  (0) 2024.04.11
BOJ-1182 부분수열의 합 Swift  (0) 2024.04.10
BOJ-15686 치킨 배달 Swift  (0) 2024.04.10
BOJ-14889 스타트와 링크 Swift  (0) 2024.04.08

문제

N개의 정수로 이루어진 수열이 있을 때, 크기가 양수인 부분수열 중에서 그 수열의 원소를 다 더한 값이 S가 되는 경우의 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 정수의 개수를 나타내는 N과 정수 S가 주어진다. (1 ≤ N ≤ 20, |S| ≤ 1,000,000) 둘째 줄에 N개의 정수가 빈 칸을 사이에 두고 주어진다. 주어지는 정수의 절댓값은 100,000을 넘지 않는다.

출력

첫째 줄에 합이 S가 되는 부분수열의 개수를 출력한다.

내가 푼 풀이

- 주어진 수열을 1부터 N만큼 뽑아서 합산한 값이 S가 된다면 경우의 수를 더한다.

- 수열을 뽑는건 조합을 이용한다.

 

import Foundation

// 입력받기
let input = readLine()!.split(separator: " ").map{Int(String($0))!}
let N = input[0], S = input[1]
let nums = readLine()!.split(separator: " ").map{Int(String($0))!}
var result = 0

// 조합(숫자의 순서와 관계없이 선택)
func combi(_ targetArr: [Int],_ targetNum: Int,_ index: Int,_ arr: [Int] ) {
    if arr.count == targetNum {
        if arr.reduce(0, +) == S { result += 1 }
        return
    }
    for i in index..<targetArr.count {
        combi(targetArr, targetNum, i+1, arr + [targetArr[i]])
    }
}

for i in 1...N {
    combi(nums, i, 0, [])
}

print(result)

문제

크기가 N×N인 도시가 있다. 도시는 1×1크기의 칸으로 나누어져 있다. 도시의 각 칸은 빈 칸, 치킨집, 집 중 하나이다. 도시의 칸은 (r, c)와 같은 형태로 나타내고, r행 c열 또는 위에서부터 r번째 칸, 왼쪽에서부터 c번째 칸을 의미한다. r과 c는 1부터 시작한다.

이 도시에 사는 사람들은 치킨을 매우 좋아한다. 따라서, 사람들은 "치킨 거리"라는 말을 주로 사용한다. 치킨 거리는 집과 가장 가까운 치킨집 사이의 거리이다. 즉, 치킨 거리는 집을 기준으로 정해지며, 각각의 집은 치킨 거리를 가지고 있다. 도시의 치킨 거리는 모든 집의 치킨 거리의 합이다.

임의의 두 칸 (r1, c1)과 (r2, c2) 사이의 거리는 |r1-r2| + |c1-c2|로 구한다.

예를 들어, 아래와 같은 지도를 갖는 도시를 살펴보자.

0 2 0 1 0
1 0 1 0 0
0 0 0 0 0
0 0 0 1 1
0 0 0 1 2

0은 빈 칸, 1은 집, 2는 치킨집이다.

(2, 1)에 있는 집과 (1, 2)에 있는 치킨집과의 거리는 |2-1| + |1-2| = 2, (5, 5)에 있는 치킨집과의 거리는 |2-5| + |1-5| = 7이다. 따라서, (2, 1)에 있는 집의 치킨 거리는 2이다.

(5, 4)에 있는 집과 (1, 2)에 있는 치킨집과의 거리는 |5-1| + |4-2| = 6, (5, 5)에 있는 치킨집과의 거리는 |5-5| + |4-5| = 1이다. 따라서, (5, 4)에 있는 집의 치킨 거리는 1이다.

이 도시에 있는 치킨집은 모두 같은 프랜차이즈이다. 프렌차이즈 본사에서는 수익을 증가시키기 위해 일부 치킨집을 폐업시키려고 한다. 오랜 연구 끝에 이 도시에서 가장 수익을 많이 낼 수 있는  치킨집의 개수는 최대 M개라는 사실을 알아내었다.

도시에 있는 치킨집 중에서 최대 M개를 고르고, 나머지 치킨집은 모두 폐업시켜야 한다. 어떻게 고르면, 도시의 치킨 거리가 가장 작게 될지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N(2 ≤ N ≤ 50)과 M(1 ≤ M ≤ 13)이 주어진다.

둘째 줄부터 N개의 줄에는 도시의 정보가 주어진다.

도시의 정보는 0, 1, 2로 이루어져 있고, 0은 빈 칸, 1은 집, 2는 치킨집을 의미한다. 집의 개수는 2N개를 넘지 않으며, 적어도 1개는 존재한다. 치킨집의 개수는 M보다 크거나 같고, 13보다 작거나 같다.

출력

첫째 줄에 폐업시키지 않을 치킨집을 최대 M개를 골랐을 때, 도시의 치킨 거리의 최솟값을 출력한다.

내가 푼 풀이

- 주어진 치킨집중 M개의 치킨집을 골라 모든 집들의 치킨거리를 구하고 최솟값을 출력한다.

- M은 임의의 수가 되기때문에 1부터 M까지 될 수 있다.

- 치킨집은 조합을 이용하여 구한다.

- 구한 최솟값들을 모두 더해 치킨 거리와 값을 비교하여 작은 값으로 갱신한다.

import Foundation

// 입력
let input = readLine()!.split(separator: " ").map{Int(String($0))!}
let N = input[0], M = input[1]
var house = [(x: Int, y: Int)]()
var chickenHouse = [(x: Int, y: Int)]()
var chickenDistance = Int.max

// 치킨집과 집 좌표를 구해서 저장한다. 좌표는 1부터 시작
for i in 1...N {
    let input = readLine()!.split(separator: " ").map{Int(String($0))!}
    for j in 0..<input.count {
        if input[j] == 1 {
            house.append((x: i, y: j+1))
        }
        if input[j] == 2 {
            chickenHouse.append((x: i, y: j+1))
        }
    }
}

// 조합, 원하는 갯수만큼 뽑았다면 치킨거리 계산하기
func combi(_ targetArr: [(x: Int, y: Int)],_ targetNum: Int,_ index: Int, arr: [(x: Int, y: Int)]) {
    if arr.count == targetNum {
        carculateChickenDistance(arr)
        return
    }
    
    for i in index..<targetArr.count {
        combi(targetArr, targetNum, i+1, arr: arr + [targetArr[i]])
    }
}

// 치킨거리 계산 후 최솟값 갱신
func carculateChickenDistance(_ arr: [(x: Int, y: Int)]) {
    var distance = [Int]()
    
    for i in 0..<house.count {
        var result = [Int]()
        for j in 0..<arr.count {
            result.append(abs(house[i].x - arr[j].x) + abs(house[i].y - arr[j].y))
        }
        distance.append(result.min()!)
    }
    chickenDistance = min(chickenDistance, distance.reduce(0, +))
}


for i in 1...M {
    combi(chickenHouse, i, 0, arr: [])
}
print(chickenDistance)

 

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

BOJ-1759 암호 만들기 Swift  (0) 2024.04.10
BOJ-1182 부분수열의 합 Swift  (0) 2024.04.10
BOJ-14889 스타트와 링크 Swift  (0) 2024.04.08
BOJ-14888 연산자끼워넣기 Swift  (0) 2024.04.08
BOJ-2309 일곱 난쟁이 Swift  (0) 2024.04.08

문제

오늘은 스타트링크에 다니는 사람들이 모여서 축구를 해보려고 한다. 축구는 평일 오후에 하고 의무 참석도 아니다. 축구를 하기 위해 모인 사람은 총 N명이고 신기하게도 N은 짝수이다. 이제 N/2명으로 이루어진 스타트 팀과 링크 팀으로 사람들을 나눠야 한다.

BOJ를 운영하는 회사 답게 사람에게 번호를 1부터 N까지로 배정했고, 아래와 같은 능력치를 조사했다. 능력치 Sij는 i번 사람과 j번 사람이 같은 팀에 속했을 때, 팀에 더해지는 능력치이다. 팀의 능력치는 팀에 속한 모든 쌍의 능력치 Sij의 합이다. Sij는 Sji와 다를 수도 있으며, i번 사람과 j번 사람이 같은 팀에 속했을 때, 팀에 더해지는 능력치는 Sij와 Sji이다.

N=4이고, S가 아래와 같은 경우를 살펴보자.


i / j  1 2 3 4
1   1 2 3
2 4   5 6
3 7 1   2
4 3 4 5  

예를 들어, 1, 2번이 스타트 팀, 3, 4번이 링크 팀에 속한 경우에 두 팀의 능력치는 아래와 같다.

  • 스타트 팀: S12 + S21 = 1 + 4 = 5
  • 링크 팀: S34 + S43 = 2 + 5 = 7

1, 3번이 스타트 팀, 2, 4번이 링크 팀에 속하면, 두 팀의 능력치는 아래와 같다.

  • 스타트 팀: S13 + S31 = 2 + 7 = 9
  • 링크 팀: S24 + S42 = 6 + 4 = 10

축구를 재미있게 하기 위해서 스타트 팀의 능력치와 링크 팀의 능력치의 차이를 최소로 하려고 한다. 위의 예제와 같은 경우에는 1, 4번이 스타트 팀, 2, 3번 팀이 링크 팀에 속하면 스타트 팀의 능력치는 6, 링크 팀의 능력치는 6이 되어서 차이가 0이 되고 이 값이 최소이다.

입력

첫째 줄에 N(4 ≤ N ≤ 20, N은 짝수)이 주어진다. 둘째 줄부터 N개의 줄에 S가 주어진다. 각 줄은 N개의 수로 이루어져 있고, i번 줄의 j번째 수는 Sij 이다. Sii는 항상 0이고, 나머지 Sij는 1보다 크거나 같고, 100보다 작거나 같은 정수이다.

출력

첫째 줄에 스타트 팀과 링크 팀의 능력치의 차이의 최솟값을 출력한다.

내가 푼 풀이

- 이 문제는 무식하게 완전탐색방식으로 풀었다.

- N명의 인원이 주어지면 두팀으로 갈라지는데 팀당 N/2인원이 정해진다.

- 단 두팀이므로 한팀이 정해지면 자연스럽게 다른 한팀이 정해진다.

- 그래서 우리는 한팀만 정해지는 모든 경우의 수를 구하고 구한 팀과 다른 팀과의 능력치를 비교했다.

- 순열을 이용해 팀을 뽑아도 좋았지만 코드로 구현하기 어려워 dfs방식으로 팀을 꾸렸다.

 

import Foundation

// 입력받기
let N = Int(readLine()!)!
var S = [[Int]]()
var answer = Int.max

// 인덱스 접근 편의상 [0] 공백을 추가했다.
S.append([0])
for i in 0..<N {
    S.append([0] + readLine()!.split(separator: " ").map{Int(String($0))!})
}

// dfs
func dfs(index: Int, team: [Int]) {
    // 팀이 꾸려지면 능력치 계산
    if team.count == N / 2 {
        calculate(arr: team)
    } else {
        // 팀 꾸리기
        for i in 1..<N {
            if index + i <= N {
                dfs(index: index+i, team: team + [index+i])
            }
        }
    }
}
// 능력치 계산
func calculate(arr: [Int]) {
    var start = arr
    var link = [Int]()
    var startSum = 0
    var linkSum = 0
    // 구해진팀과 다른 팀
    for i in 1...N {
        if !start.contains(i) {
            link.append(i)
        }
    }
    // 능력치를 모두 더해준다.
    for i in 0..<N/2 {
        for j in 0..<N/2 {
            startSum += S[start[i]][start[j]]
            linkSum += S[link[i]][link[j]]
        }
    }

    answer = min(answer, abs(startSum - linkSum))
}

for i in 1...N {
    dfs(index: i, team: [i])
}

print(answer)

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

BOJ-1182 부분수열의 합 Swift  (0) 2024.04.10
BOJ-15686 치킨 배달 Swift  (0) 2024.04.10
BOJ-14888 연산자끼워넣기 Swift  (0) 2024.04.08
BOJ-2309 일곱 난쟁이 Swift  (0) 2024.04.08
BOJ-9663 N-Queen Swift  (1) 2024.04.07

출처:https://namu.wiki/w/%EB%84%88%EB%B9%84%20%EC%9A%B0%EC%84%A0%20%ED%83%90%EC%83%89

깊이 우선 탐색(DFS)

- 루트노드에서 시작하여 다음 분기로 넘어가기전 해당 분기를 완벽하게 탐색하는 방법.

- 해당 분기를 모두 탐색한 후, 이 분기는 이미 탐색함을 기록하고 다음분기로 넘어감

- 백트래킹으로 이용한다.

- 스택 또는 재귀함수로 구현한다.

-> 모든 노드를 방문하고자 할때 주로 사용함

-> BFS에 비해 상대적으로 검색속도가 느림

-> 구현은 DFS가 더 간단하다.

 

너비 우선 탐색(BFS)

- 루트노드 혹은 임의노드부터 시작하여 인접한 가까운 노드부터 탐색하고 마지막으로 가장 먼 노드를 탐색하는 방법

- 주로 노드사이의 최단거리를 계산할때 사용한다.

- 큐로 구현한다.

 

ex) 지구상의 모든 관계를 그래프로 표현했을때, 철수와 영희와의 관계를 찾는다면

깊이 우선탐색: 모든 관계를 탐색하며 찾는다.

너비 우선탐색: 철수의 가까운 관계부터 찾는다.

 

DFS와 BFS의 시간복잡도

모든 노드를 탐색한다는점에서 DFS와 BFS의 시간복잡도는 동일하다.

구현할때의 이용한 자료구조에 따라서 전체 시간복잡도는 다르게 될 수 있다.

N은 노드, E는 걸리는 시간 이라 한다면

인접리스트: O(N+E)

인접행렬: O(N^2)

 

DFS와 BFS의 활용유형

1. 그래프의 모든 접점을 방문해야 할 때

- 이는 둘다 비슷한 성능을 내므로 알맞은 자료구조로 시간을 줄여서 구현한다.

 

2. 경로의 특징을 저장해야할 때

- 백트래킹과 같은 노드를 탐색했을때, 조건에 맞지않아서 이를 기록하고 더이상 노드탐색을 하지않을때나 탐색하는데 있어서 여러가지 특징이나 조건을 저장하고 탐색할때 DFS를 이용한다.

 

3. 최단거리를 구할 때

- 미로찾기와 같은 최단거리를 구할 때 BFS를 이용하면 좋다.

- DFS를 이용한다면 모든 노드를 방문하게 될 수 있기때문이다.

 

DFS 구현 Swift

// 소스코드 출처: 개발자 소들이 https://babbab2.tistory.com/107

func dfs(graph: [String: [String]], start: String) -> [String]{
    // 이미 방문한 배열
    var visitedQueue = [String]()
    // 방문해야하는 배열
    var needVisitQueue = [start]
    
    // DFS
    // 분기에 가장 깊숙한 곳까지 탐색하기 때문에 removeLast()
    while !needVisitQueue.isEmpty {
        let node = needVisitQueue.removeLast()
        if visitedQueue.contains(node) { continue }
        
        visitedQueue.append(node)
        needVisitQueue += graph[node] ?? []
    }
    return visitedQueue
}

 

BFS 구현 Swift

// 소스코드 출처: 개발자 소들이 https://babbab2.tistory.com/106

func bfs(graph: [String: [String]], start: String) -> [String]{
    // 이미 방문한 배열
    var visitedQueue = [String]()
    // 방문해야하는 배열
    var needVisitQueue = [start]
    
    // BFS
    // 분기에 가장 인접한 곳 부터 탐색하기 때문에 removeFirst()
    while !needVisitQueue.isEmpty {
        let node = needVisitQueue.removeFirst()
        if visitedQueue.contains(node) { continue }
        
        visitedQueue.append(node)
        needVisitQueue += graph[node] ?? []
    }
    return visitedQueue
}

 

 

Reference:

https://velog.io/@lucky-korma/DFS-BFS%EC%9D%98-%EC%84%A4%EB%AA%85-%EC%B0%A8%EC%9D%B4%EC%A0%90

 

DFS, BFS의 설명, 차이점

그래프란, 정점(node)과 그 정점을 연결하는 간선(edge)으로 이루어진 자료구조의 일종을 말하며,그래프를 탐색한다는 것은 하나의 정점으로부터 시작하여 차례대로 모든 정점들을 한 번씩 방문하

velog.io

https://devuna.tistory.com/32

 

[알고리즘] 깊이 우선 탐색(DFS) 과 너비 우선 탐색(BFS)

[알고리즘] 깊이 우선 탐색(DFS)과 너비 우선 탐색(BFS) 그래프를 탐색하는 방법에는 크게 깊이 우선 탐색(DFS)과 너비 우선 탐색(BFS)이 있습니다. 📌여기서 그래프란, 정점(node)과 그 정점을 연결하

devuna.tistory.com

https://babbab2.tistory.com/107

 

Swift) 깊이 우선 탐색(DFS) 구현 해보기

안녕하세요 :) 소들입니다!!! 이번 포스팅에선 깊이 우선 탐색(DFS)에 대해 알아보려고 해요!!!! 깊이 우선 탐색이란, 너비 우선 탐색(BFS)과 마찬가지로 그래프를 탐색하는 방법 중 하나인데, 그래

babbab2.tistory.com

 

'코딩테스트 > 알고리즘' 카테고리의 다른 글

투포인터 알고리즘 Swift  (0) 2024.04.14
순열과 조합 Swift  (0) 2024.04.10
이분탐색 Swift  (1) 2024.04.06
동적계획법 DP(Dynamic Programming)  (0) 2023.04.20
최대공약수와 최소공배수 Swift  (0) 2023.04.07

문제

N개의 수로 이루어진 수열 A1, A2, ..., AN이 주어진다. 또, 수와 수 사이에 끼워넣을 수 있는 N-1개의 연산자가 주어진다. 연산자는 덧셈(+), 뺄셈(-), 곱셈(×), 나눗셈(÷)으로만 이루어져 있다.

우리는 수와 수 사이에 연산자를 하나씩 넣어서, 수식을 하나 만들 수 있다. 이때, 주어진 수의 순서를 바꾸면 안 된다.

예를 들어, 6개의 수로 이루어진 수열이 1, 2, 3, 4, 5, 6이고, 주어진 연산자가 덧셈(+) 2개, 뺄셈(-) 1개, 곱셈(×) 1개, 나눗셈(÷) 1개인 경우에는 총 60가지의 식을 만들 수 있다. 예를 들어, 아래와 같은 식을 만들 수 있다.

  • 1+2+3-4×5÷6
  • 1÷2+3+4-5×6
  • 1+2÷3×4-5+6
  • 1÷2×3-4+5+6

식의 계산은 연산자 우선 순위를 무시하고 앞에서부터 진행해야 한다. 또, 나눗셈은 정수 나눗셈으로 몫만 취한다. 음수를 양수로 나눌 때는 C++14의 기준을 따른다. 즉, 양수로 바꾼 뒤 몫을 취하고, 그 몫을 음수로 바꾼 것과 같다. 이에 따라서, 위의 식 4개의 결과를 계산해보면 아래와 같다.

  • 1+2+3-4×5÷6 = 1
  • 1÷2+3+4-5×6 = 12
  • 1+2÷3×4-5+6 = 5
  • 1÷2×3-4+5+6 = 7

N개의 수와 N-1개의 연산자가 주어졌을 때, 만들 수 있는 식의 결과가 최대인 것과 최소인 것을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 둘째 줄에는 A1, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수, 곱셈(×)의 개수, 나눗셈(÷)의 개수이다.

출력

첫째 줄에 만들 수 있는 식의 결과의 최댓값을, 둘째 줄에는 최솟값을 출력한다. 연산자를 어떻게 끼워넣어도 항상 -10억보다 크거나 같고, 10억보다 작거나 같은 결과가 나오는 입력만 주어진다. 또한, 앞에서부터 계산했을 때, 중간에 계산되는 식의 결과도 항상 -10억보다 크거나 같고, 10억보다 작거나 같다.

내가 푼 풀이

- 첫번째 방법으로 연산자는 왼쪽연산과 오른쪽연산으로 처음엔 그리디를 이용해 서로 최소가되면 최솟값, 서로 최대가되면 최댓값이 나올꺼라 예상했는데, 정답이 나오지않았다.

- 두번째 방법으로 아예 완전탐색을 해보는것인데, dfs를 이용해 연산자가 남아있을때까지 모든 경우의 수를 구해보는것 이었다.

 

import Foundation

// 입력받기
let N = Int(readLine()!)!
let A = readLine()!.split(separator: " ").map{Int(String($0))!}
var op = readLine()!.split(separator: " ").map{Int(String($0))!}
var maxNum = -1000000000
var minNum = 1000000000

// dfs
func dfs(num: Int, sum: Int) {
    if num == N-1 {
        maxNum = max(sum, maxNum)
        minNum = min(sum, minNum)
    } else {
        if op[0] > 0 {
            op[0] -= 1
            dfs(num: num+1, sum: sum + A[num+1])
            op[0] += 1
        }
        if op[1] > 0 {
            op[1] -= 1
            dfs(num: num+1, sum: sum - A[num+1])
            op[1] += 1
        }
        if op[2] > 0 {
            op[2] -= 1
            dfs(num: num+1, sum: sum * A[num+1])
            op[2] += 1
        }
        if op[3] > 0 {
            op[3] -= 1
            dfs(num: num+1, sum: sum / A[num+1])
            op[3] += 1
        }
    }
}

dfs(num: 0, sum: A[0])
print(maxNum)
print(minNum)

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

BOJ-15686 치킨 배달 Swift  (0) 2024.04.10
BOJ-14889 스타트와 링크 Swift  (0) 2024.04.08
BOJ-2309 일곱 난쟁이 Swift  (0) 2024.04.08
BOJ-9663 N-Queen Swift  (1) 2024.04.07
BOJ-7568 덩치 Swift  (1) 2024.04.07

문제

왕비를 피해 일곱 난쟁이들과 함께 평화롭게 생활하고 있던 백설공주에게 위기가 찾아왔다. 일과를 마치고 돌아온 난쟁이가 일곱 명이 아닌 아홉 명이었던 것이다.

아홉 명의 난쟁이는 모두 자신이 "백설 공주와 일곱 난쟁이"의 주인공이라고 주장했다. 뛰어난 수학적 직관력을 가지고 있던 백설공주는, 다행스럽게도 일곱 난쟁이의 키의 합이 100이 됨을 기억해 냈다.

아홉 난쟁이의 키가 주어졌을 때, 백설공주를 도와 일곱 난쟁이를 찾는 프로그램을 작성하시오.

입력

아홉 개의 줄에 걸쳐 난쟁이들의 키가 주어진다. 주어지는 키는 100을 넘지 않는 자연수이며, 아홉 난쟁이의 키는 모두 다르며, 가능한 정답이 여러 가지인 경우에는 아무거나 출력한다.

출력

일곱 난쟁이의 키를 오름차순으로 출력한다. 일곱 난쟁이를 찾을 수 없는 경우는 없다.

내가 푼 풀이

- 일곱난쟁이의 키 합이 100으로 만들어야한다.

- 주어진 입력은 아홉난쟁이이므로, 아홉난쟁이의 합에서 두 난쟁이만 제거한 값이 100이된다면, 남은 일곱난쟁이가 정답이 된다.

import Foundation

// 입력받기
var lettles = [Int]()
for i in 0..<9 {
    lettles.append(Int(readLine()!)!)
}
lettles.sort(by: <)
// 아홉난쟁이키의 합과 난쟁이가 아닌 인덱스 저장용 튜플
var total = lettles.reduce(0, +)
var notLettlesIndex: (index1: Int, index2: Int) = (0,0)

// 완전탐색
for i in 0..<9 {
    var isFind = false
    for j in i+1..<9 {
        let sum = lettles[i] + lettles[j]
        if total - sum == 100 {
            isFind = true
            notLettlesIndex = (i,j)
        }
    }
    if isFind {
        break
    }
}

// 해당 난쟁이를 인덱스를 통해 제거하고, 위 2중 반복문 특성상 무조건 i < j 이므로 index1 < index2 이다.
// 반복문에 중간값이 제거가된다면 전체 인덱스가 -1로 변경되므로 참고해서 제거해준다.
lettles.remove(at: notLettlesIndex.index1)
lettles.remove(at: notLettlesIndex.index2-1)
for i in lettles {
    print(i)
}

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

BOJ-14889 스타트와 링크 Swift  (0) 2024.04.08
BOJ-14888 연산자끼워넣기 Swift  (0) 2024.04.08
BOJ-9663 N-Queen Swift  (1) 2024.04.07
BOJ-7568 덩치 Swift  (1) 2024.04.07
BOJ-2805 나무 자르기 Swift  (1) 2024.04.07

문제

N-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다.

N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N이 주어진다. (1 ≤ N < 15)

출력

첫째 줄에 퀸 N개를 서로 공격할 수 없게 놓는 경우의 수를 출력한다.

내가 푼 풀이

- 첫번째 고안한방법은 이차원 배열의 보드를 만들어 전부 순회하며 가능한 모든 경우의수를 구하려했다.

- 이는 O(n^n) 이나 걸리는 너무나 느린 알고리즘이였고, 역시나 시간초과가 떴다.

- 시간을 줄이는 방법을 모르겠어서, 다른 풀이를 참고해보니 이 문제는 백트래킹 문제였다.

- 또한 배열도 이차원 배열이 아닌 일차원 배열로도 풀 수 있다는걸 몰랐다.

 

-> 퀸의 공격범위 특성상 주어진 체스판에 같은 행에 퀸 두개 이상을 배치할 수 없다.

-> 우리는 배치된 퀸의 열과 대각선 방향만 확인하면 된다.

-> 여기서 일차원배열은 board[a] = b 라 하면 이는 a행 b열에 퀸을 배치한다. 라고 의미한다.

-> 같은 행에 두개이상 배치할 수 없기 때문에 1행부터 N행까지 순회하고, 각각 열마다 퀸을 배치해본다..

-> 퀸을 배치하고, 다음 행의 퀸을 배치할 수 있는 범위가 있는지 파악하고, 배치가 가능하다면 퀸을 배치하고 다음 행으로 이동한다.

-> 퀸을 배치할 범위가 존재하지 않다면 다시 돌아와 다른 배치가능한 곳에 배치한다.

-> 이렇게 퀸을 N개만큼 배치했다면 경우의 수 1을 올린다.

-> 여기서 배열 인덱스 특성상 두 원소 (x1, y1), (x2, y2)가 있을때, |x1- x2| == |y1-y2| 가 성립한다면 두 원소는 대각선 위치에 인접해있다고 한다.

0,0 0,1 0,2
1,0 1,1 1,2
2,0 2,1 2,2

배열에서 (1,1) 과 (0,2) 는 |1 - 0| == |1-2| 가 성립하므로 두 원소는 대각선 위치에 있다고 할 수 있다.

위 특성을 이용하여 퀸을 배치한다면 배치한 퀸의 대각선의 위치도 알 수 있게 된다.

 

import Foundation
// 입력받기
let N = Int(readLine()!)!
var arr = Array(repeating: -1, count: N)
var visited = Array(repeating: false, count: N)
var result = 0

// 범위 확인
func checkAttakable(index: Int) -> Bool {
    for i in 0..<index {
        // arr[index] == arr[i] : 같은 열에 존재하는가?
        // abs(arr[index] - arr[i]) == abs(index - i) : 대각선방향으로 존재하는가?
        if arr[index] == arr[i] || abs(arr[index] - arr[i]) == abs(index - i) {
            return false
        }
    }
    return true
}

// DFS
func dfs(index: Int) {
    // 모든 행에 배치됬다면 탈출
    if index == N {
        result += 1
        return
    }
    // 주어진 행의 열에 모두 배치해본다.
    for i in 0..<N {
        // 이미 배치된 퀸의 공격범위라면 패스
        if visited[i] { continue }
        arr[index] = i
        // 배치 가능한 곳인지 확인한다.
        if checkAttakable(index: index) {
            visited[i] = true
            // 배치 후 그다음 행에 배치하귀위해 dfs 재귀호출
            dfs(index: index+1)
            // 그 전에 탐색했던 범위를 모두 배치가능으로 돌려놓는다.(백트래킹을 위해)
            visited[i] = false
        }
    }
}

dfs(index: 0)
print(result)

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

BOJ-14888 연산자끼워넣기 Swift  (0) 2024.04.08
BOJ-2309 일곱 난쟁이 Swift  (0) 2024.04.08
BOJ-7568 덩치 Swift  (1) 2024.04.07
BOJ-2805 나무 자르기 Swift  (1) 2024.04.07
BOJ-1072 게임 Swift  (1) 2024.04.06

+ Recent posts