문제

N개의 수 중에서 어떤 수가 다른 수 두 개의 합으로 나타낼 수 있다면 그 수를 “좋다(GOOD)”고 한다.

N개의 수가 주어지면 그 중에서 좋은 수의 개수는 몇 개인지 출력하라.

수의 위치가 다르면 값이 같아도 다른 수이다.

입력

첫째 줄에는 수의 개수 N(1 ≤ N ≤ 2,000), 두 번째 줄에는 i번째 수를 나타내는 Ai가 N개 주어진다. (|Ai| ≤ 1,000,000,000, Ai는 정수)

출력

좋은 수의 개수를 첫 번째 줄에 출력한다.

내가 푼 풀이

- N개의 수를 순회하면서 투포인터 알고리즘으로 합이 같은지 찾았다.

- 투포인터 알고리즘을 사용하기 전에 A정렬을 오름차순으로 정렬후, start: 첫번째 end: 마지막번째 위치로 이용했다.

- 합산한 값이 크다면 end를 줄이고, 작다면 start를 늘려 찾고, 같은 인덱스(같은원소인경우) 패스했다.

 

import Foundation

// 입력받기
let N = Int(readLine()!)!
var A = readLine()!.split(separator: " ").map{Int(String($0))!}
var count = 0
A.sort()

for i in 0..<N {
    var num = A[i]
    var start = 0
    var end = A.count - 1
    // 투포인터 알고리즘을 이용
    // 합산한 값이 작다면 start + 1
    // 합산한 값이 크다면 end - 1
    while start < end {
        if start == i {
            start += 1
            continue
        } else if end == i {
            end -= 1
            continue
        }
        let sum = A[start] + A[end]
        
        if sum == num {
            count += 1
            break
        }
        if sum > num {
            end -= 1
        } else {
            start += 1
        }
    }
    
}
print(count)

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

BOJ-1013 Contact Swift  (0) 2024.04.15
BOJ-5430 AC Swift  (1) 2024.04.15
BOJ-5052 전화번호 목록 Swift  (0) 2024.04.14
BOJ-2470 두 용액 Swift  (0) 2024.04.14
BOJ-14503 로봇 청소기 Swift  (0) 2024.04.13

문제

전화번호 목록이 주어진다. 이때, 이 목록이 일관성이 있는지 없는지를 구하는 프로그램을 작성하시오.

전화번호 목록이 일관성을 유지하려면, 한 번호가 다른 번호의 접두어인 경우가 없어야 한다.

예를 들어, 전화번호 목록이 아래와 같은 경우를 생각해보자

  • 긴급전화: 911
  • 상근: 97 625 999
  • 선영: 91 12 54 26

이 경우에 선영이에게 전화를 걸 수 있는 방법이 없다. 전화기를 들고 선영이 번호의 처음 세 자리를 누르는 순간 바로 긴급전화가 걸리기 때문이다. 따라서, 이 목록은 일관성이 없는 목록이다.

입력

첫째 줄에 테스트 케이스의 개수 t가 주어진다. (1 ≤ t ≤ 50) 각 테스트 케이스의 첫째 줄에는 전화번호의 수 n이 주어진다. (1 ≤ n ≤ 10000) 다음 n개의 줄에는 목록에 포함되어 있는 전화번호가 하나씩 주어진다. 전화번호의 길이는 길어야 10자리이며, 목록에 있는 두 전화번호가 같은 경우는 없다.

출력

각 테스트 케이스에 대해서, 일관성 있는 목록인 경우에는 YES, 아닌 경우에는 NO를 출력한다.

내가 푼 풀이

- 전화번호가 차례로주어지는것이 아니라 이미 목록에 있고, 정렬할 수 있다.

- 전화번호를 숫자형식으로 오름차순 정렬하는것과 문자형식으로 정렬하는것이 결과가 다르다.

"""
113
12340
123440
12345
98346
문자열배열 오름차순정렬: ["113", "12340", "123440", "12345", "98346"]
숫자배열 오름차순정렬:   [113, 12340, 12345, 98346, 123440]
"""

- 이전의 번호가 접두사로 존재한다면 이 목록은 일관성이 없다고 한다.

- 숫자배열과 다르게 문자열배열의 정렬은 각각 자리마다의 문자를 비교하므로 접두사를 찾기에 더 알맞은 정렬방법이 된다.

- 이렇게 정렬된 문자열배열을 순회하며 i+1번째 문자열의 접두사가 i번째문자인지만 파악하면 된다.

 

import Foundation

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

for i in 0..<t {
    let n = Int(readLine()!)!
    var str = [String]()
    var result = true
    for j in 0..<n {
        let num = readLine()!
        str.append(num)
    }
    str.sort(by: <)
    for j in 0..<str.count-1 {
        if str[j+1].hasPrefix(str[j]) {
            result = false
            break
        }
    }
    if result {
        print("YES")
    } else {
        print("NO")
    }
}

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

BOJ-5430 AC Swift  (1) 2024.04.15
BOJ-1253 좋다 Swift  (0) 2024.04.14
BOJ-2470 두 용액 Swift  (0) 2024.04.14
BOJ-14503 로봇 청소기 Swift  (0) 2024.04.13
BOJ-1011 Fly me to the Alpha Centauri Swift  (0) 2024.04.13

문제

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

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

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

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

입력

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

출력

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

내가 푼 풀이

- N은 최대 100,000으로 O(N^2)로는 무조건 시간초과가 뜰꺼라 생각했다.

- 투포인터 알고리즘을 이용해서 각각 원소끼리 더해주고,

더한값이 양수라면 0에 가까워지기 위해 오른쪽인덱스를 왼쪽으로, 더한값이 음수라면 0에 가까워지기 위해 왼쪽인덱스를 오른쪽으로 이동한다.

- 더한값의 절댓값이 0에 가까워지면 따로 원소를 저장하고, 0이라면 바로 저장 후 탈출한다.

 

import Foundation

// 입력받기
let N = Int(readLine()!)!
var arr = readLine()!.split(separator: " ").map{Int(String($0))!}
var result = Int.max
var resultArr = [Int]()
var start = 0
var end = arr.count - 1
arr.sort(by: <)

// 왼쪽인덱스 start, 오른쪽인덱스 end가 서로 만날때 까지
while start < end {
    let leftNum = arr[start]
    let rightNum = arr[end]
    let sum = leftNum + rightNum
    
    // 더한값이 양수라면 오른쪽인덱스를 왼쪽으로 움직여 오른쪽 원소값을 더 작게만든다
    // 더한값이 음수라면 왼쪽인덱스를 오른쪽으로 움직여 왼쪽 원소값을 더 크게만든다.
    if sum > 0 {
        end -= 1
    } else if sum < 0 {
        start += 1
    } else {
        resultArr = [leftNum, rightNum]
        break
    }
    // 더한값의 절댓값이 이전에 구한 값보다 작아지면 갱신
    if abs(sum) < result {
        result = abs(sum)
        resultArr = [leftNum, rightNum]
    }
    
}
print(resultArr.map{String($0)}.joined(separator: " "))

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

BOJ-1253 좋다 Swift  (0) 2024.04.14
BOJ-5052 전화번호 목록 Swift  (0) 2024.04.14
BOJ-14503 로봇 청소기 Swift  (0) 2024.04.13
BOJ-1011 Fly me to the Alpha Centauri Swift  (0) 2024.04.13
BOJ-10974 모든 순열 Swift  (0) 2024.04.13

문제

로봇 청소기와 방의 상태가 주어졌을 때, 청소하는 영역의 개수를 구하는 프로그램을 작성하시오.

로봇 청소기가 있는 방은 N X M 크기의 직사각형으로 나타낼 수 있으며, 1×1 크기의 정사각형 칸으로 나누어져 있다. 각각의 칸은 벽 또는 빈 칸이다. 청소기는 바라보는 방향이 있으며, 이 방향은 동, 서, 남, 북 중 하나이다. 방의 각 칸은 좌표 (로 나타낼 수 있고, 가장 북쪽 줄의 가장 서쪽 칸의 좌표가 (0,0) 가장 남쪽 줄의 가장 동쪽 칸의 좌표가 (이다. 즉, 좌표 는 북쪽에서 번째에 있는 줄의 서쪽에서 번째 칸을 가리킨다. 처음에 빈 칸은 전부 청소되지 않은 상태이다.

로봇 청소기는 다음과 같이 작동한다.

  1. 현재 칸이 아직 청소되지 않은 경우, 현재 칸을 청소한다.
  2. 현재 칸의 주변 4칸 중 청소되지 않은 빈 칸이 없는 경우,
    1. 바라보는 방향을 유지한 채로 한 칸 후진할 수 있다면 한 칸 후진하고 1번으로 돌아간다.
    2. 바라보는 방향의 뒤쪽 칸이 벽이라 후진할 수 없다면 작동을 멈춘다.
  3. 현재 칸의 주변 4칸 중 청소되지 않은 빈 칸이 있는 경우,
    1. 반시계 방향으로 90도 회전한다.
    2. 바라보는 방향을 기준으로 앞쪽 칸이 청소되지 않은 빈 칸인 경우 한 칸 전진한다.
    3. 1번으로 돌아간다.

입력

첫째 줄에 방의 크기 이 입력된다. (3≤N,M≤50)  둘째 줄에 처음에 로봇 청소기가 있는 칸의 좌표 와 처음에 로봇 청소기가 바라보는 방향 d가 입력된다.d가 0인 경우 북쪽, 1인 경우 동쪽, 2인 경우 남쪽, 3인 경우 서쪽을 바라보고 있는 것이다.

셋째 줄부터 개의 줄에 각 장소의 상태를 나타내는 N X M개의 값이 한 줄에 개씩 입력된다. 번째 줄의 번째 값은 칸 의 상태를 나타내며, 이 값이 0인 경우가 청소되지 않은 빈 칸이고, 1인 경우 에 벽이 있는 것이다. 방의 가장 북쪽, 가장 남쪽, 가장 서쪽, 가장 동쪽 줄 중 하나 이상에 위치한 모든 칸에는 벽이 있다. 로봇 청소기가 있는 칸은 항상 빈 칸이다.

출력

로봇 청소기가 작동을 시작한 후 작동을 멈출 때까지 청소하는 칸의 개수를 출력한다.

내가 푼 풀이

- 문제는 어렵지않으나 해당 로봇의 작동을 구현하는데 좀 생각이 많아졌다.

- 작동할 수 없을때 까지 최대로 돌려서 청소한 구역의 수를 구하는문제다.

- 해당 로봇의 작동을 함수로 정하고, while을 무한으로 돌려 탈출조건(4구역이 청소되거나 벽인상태에서 후진할수 없는 상태)이 성립하면 나오도록 구현하였다.

 

import Foundation

// 입력받기
let input = readLine()!.split(separator: " ").map{Int(String($0))!}
let N = input[0], M = input[1]
let input2 = readLine()!.split(separator: " ").map{Int(String($0))!}
var robotLoc = (x: input2[0], y: input2[1])
var dir = input2[2]
var room = [[Int]]()
var count = 0
for i in 0..<N {
    room.append(readLine()!.split(separator: " ").map{Int(String($0))!})
}

// 탈출조건이 만족할때 까지 청소
while true {
    if !find(loc: &robotLoc, dir: &dir) {
        break
    }
}
// 청소된 방의 구역수를 세고 출력
for i in 0..<room.count {
    count += room[i].filter{ $0 == -1}.count
}
print(count)

// 구역 청소
func cleaning(loc: (x: Int, y: Int)) {
    if room[loc.x][loc.y] == 0 {
        room[loc.x][loc.y] = -1
    }
}

// 인접한 4개의 구역 확인
func find(loc: inout (x: Int, y: Int), dir: inout Int) -> Bool{
    // 4개 구역 임의의 수입력
    var upp = -2, rightp = -2, downp = -2, leftp = -2
    // 청소할수 없다 (벽이거나 이미 청소된곳)
    var cant = [1,-1]
    // 처음에 주어지는 위치는 청소되지않는곳으로 부여되므로 일단 청소
    cleaning(loc: loc)
    
    // 주어진 위치의 상 하 좌 우 위치를 구한다.
    // 인덱스 범위를 넘어도 벽(1) 로 설정
    if loc.x-1 >= 0 {
        upp = room[loc.x-1][loc.y]
    } else { upp = 1 }
    
    if loc.y+1 < M {
        rightp = room[loc.x][loc.y+1]
    } else { rightp = 1 }
    
    if loc.x+1 < N {
        downp = room[loc.x+1][loc.y]
    } else { downp = 1 }
    
    if loc.y-1 >= 0 {
        leftp = room[loc.x][loc.y-1]
    } else { leftp = 1 }
    
    // 4개 인접한 구역이 청소할 수 없는경우(이미청소됬거나, 벽인 경우)
    if cant.contains(upp) && cant.contains(downp) && cant.contains(leftp) && cant.contains(rightp) {
        // 방향에 따라 후진한다.
        // 후진 할 수 없는경우 로봇의 작동 멈춤
        if dir == 0 {
            if downp == 1 {
                return false
            } else {
                loc.x += 1
                return true
            }
        }
        if dir == 1 {
            if leftp == 1 {
                return false
            } else {
                loc.y -= 1
                return true
            }
        }
        if dir == 2 {
            if upp == 1 {
                return false
            } else {
                loc.x -= 1
                return true
            }
        }
        if dir == 3 {
            if rightp == 1 {
                return false
            } else {
                loc.y += 1
                return true
            }
        }
    } else {
        // 4군데중 청소할 곳이 있는경우
        // 방향에 따라 위치로 이동하고 청소
        dir -= 1
        if dir == -1 {
            dir = 3
        }
        if dir == 0 {
            if upp == 0 {
                loc.x -= 1
                cleaning(loc: loc)
            }
        } else if dir == 1 {
            if rightp == 0 {
                loc.y += 1
                cleaning(loc: loc)
            }
        } else if dir == 2 {
            if downp == 0 {
                loc.x += 1
                cleaning(loc: loc)
            }
        }else {
            if leftp == 0 {
                loc.y -= 1
                cleaning(loc: loc)
            }
        }
        return true
    }
    return true
}

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

BOJ-5052 전화번호 목록 Swift  (0) 2024.04.14
BOJ-2470 두 용액 Swift  (0) 2024.04.14
BOJ-1011 Fly me to the Alpha Centauri Swift  (0) 2024.04.13
BOJ-10974 모든 순열 Swift  (0) 2024.04.13
BOJ-15683 감시 Swift  (1) 2024.04.12

문제

우현이는 어린 시절, 지구 외의 다른 행성에서도 인류들이 살아갈 수 있는 미래가 오리라 믿었다. 그리고 그가 지구라는 세상에 발을 내려 놓은 지 23년이 지난 지금, 세계 최연소 ASNA 우주 비행사가 되어 새로운 세계에 발을 내려 놓는 영광의 순간을 기다리고 있다.

그가 탑승하게 될 우주선은 Alpha Centauri라는 새로운 인류의 보금자리를 개척하기 위한 대규모 생활 유지 시스템을 탑재하고 있기 때문에, 그 크기와 질량이 엄청난 이유로 최신기술력을 총 동원하여 개발한 공간이동 장치를 탑재하였다. 하지만 이 공간이동 장치는 이동 거리를 급격하게 늘릴 경우 기계에 심각한 결함이 발생하는 단점이 있어서, 이전 작동시기에 k광년을 이동하였을 때는 k-1 , k 혹은 k+1 광년만을 다시 이동할 수 있다. 예를 들어, 이 장치를 처음 작동시킬 경우 -1 , 0 , 1 광년을 이론상 이동할 수 있으나 사실상 음수 혹은 0 거리만큼의 이동은 의미가 없으므로 1 광년을 이동할 수 있으며, 그 다음에는 0 , 1 , 2 광년을 이동할 수 있는 것이다. ( 여기서 다시 2광년을 이동한다면 다음 시기엔 1, 2, 3 광년을 이동할 수 있다. )

김우현은 공간이동 장치 작동시의 에너지 소모가 크다는 점을 잘 알고 있기 때문에 x지점에서 y지점을 향해 최소한의 작동 횟수로 이동하려 한다. 하지만 y지점에 도착해서도 공간 이동장치의 안전성을 위하여 y지점에 도착하기 바로 직전의 이동거리는 반드시 1광년으로 하려 한다.

김우현을 위해 x지점부터 정확히 y지점으로 이동하는데 필요한 공간 이동 장치 작동 횟수의 최솟값을 구하는 프로그램을 작성하라.

입력

입력의 첫 줄에는 테스트케이스의 개수 T가 주어진다. 각각의 테스트 케이스에 대해 현재 위치 x 와 목표 위치 y 가 정수로 주어지며, x는 항상 y보다 작은 값을 갖는다. (0 ≤ x < y < 231)

출력

각 테스트 케이스에 대해 x지점으로부터 y지점까지 정확히 도달하는데 필요한 최소한의 공간이동 장치 작동 횟수를 출력한다.

내가 푼 풀이

- 장치는 1씩 증가하는 등차수열의 모습으로 이동할 수 있고, 목적지 y직전 위치에서 y까지는 이동거리 1광년으로 움직여야한다.

- 작동횟수를 최소로 줄이려면 이동거리를 등차수열로 중간지점만큼 움직이고, 남은 지점은 감소하는 등차수열로 이동한다면 y직전의 지점과 y의 거리가 1이 되고 최소로 움직이게된다.

- 이것을 작동횟수의 기준으로 작동횟수당 최대로 움직일 수 있는 거리는 다음과 나타낼 수 있다.

작동횟수 최대 이동거리
1 1 = 1
2 1 1 = 2
3 1 2 1 = 4
4 1 2 2 1 = 6
5 1 2 3 2 1 = 9
6 1 2 3 3 2 1 = 12

예로 거리가 5가되는 지점을 이동할때 작동횟수의 최솟값은 4가 된다.

(4번 장치를 작동시켰을 때, 최대 이동가능거리가 6이 되기때문에)

 

위 최대 이동거리는 가운데를 중심으로 등차수열이 대칭되어있다.

홀수는 1부터 n까지의 등차수열과 n-1 부터 1까지의 등차수열이 나열되있고,

짝수는 1부터 n까지의 등차수열과 n부터 1까지의 등차수열이 나열되어있다.

등차수열의 합은 n(n+1) / 2 인 점을 이용해

홀수의 등차수열 합은 n^2 이 되고, 짝수의 등차수열 합은 n * (n + 1)이 된다.

 

작동횟수를 1부터 증가시켜가며 x와 y지점의 거리가 범위에 존재하는지 파악하고, 최솟값을 출력한다.

 

import Foundation

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

// 테스트케이스
for i in 0..<T {
    let input = readLine()!.split(separator: " ").map{Int(String($0))!}
    // 거리
    var distance = input[1] - input[0]
    // 작동횟수
    var count = 1
    // 1부터 n까지의 등차수열
    var n = 1
    while true {
        if count % 2 == 0 {
            // 작동횟수가 짝수번이라면 등차수열은 1...N과 N...1 두개가 존재
            // 최대이동거리보다 주어진 거리가 작다면 이 횟수로 이동가능하다고 판단
            if n * (n + 1) >= distance {
                print(count)
                break
            }
        } else {
            // 작동횟수가 홀수번이라면 등차수열은 1...N과 N-1...1 두개가 존재
            // 최대이동거리보다 주어진 거리가 작다면 이 횟수로 이동가능하다고 판단
            if n * n >= distance {
                print(count)
                break
            }
        }
        // 작동횟수 증가
        count += 1
        // 작동횟수가 증가하고 홀수번이 된다면 n도 1 증가한다.
        // 1번: 1 , 2번: 1 1, 3번: 1 2 1 이되므로 n = 2가 된다.
        if count % 2 != 0 {
            n += 1
        }
    }
}

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

BOJ-2470 두 용액 Swift  (0) 2024.04.14
BOJ-14503 로봇 청소기 Swift  (0) 2024.04.13
BOJ-10974 모든 순열 Swift  (0) 2024.04.13
BOJ-15683 감시 Swift  (1) 2024.04.12
BOJ-10819 차이를 최대로 Swift  (0) 2024.04.12

문제

N이 주어졌을 때, 1부터 N까지의 수로 이루어진 순열을 사전순으로 출력하는 프로그램을 작성하시오.

입력

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

출력

첫째 줄부터 N!개의 줄에 걸쳐서 모든 순열을 사전순으로 출력한다.

내가 푼 풀이

- dfs로 1부터 N까지 N개의 원소를 갖는 배열을 뽑는다.

- 1부터 원소를 뽑는다고 하면 dfs로 뽑힌 원소들은 자연스럽게 사전순으로 정렬되어있다.

 

import Foundation

let N = Int(readLine()!)!
var visited = Array(repeating: false, count: N+1)

func dfs(count: Int, arr: [Int]) {
    if count == N {
        print(arr.map{String($0)}.joined(separator: " "))
        return
    }
    
    for i in 1...N {
        if !visited[i] {
            visited[i] = true
            dfs(count: count+1, arr: arr + [i])
            visited[i] = false
        }
    }
}

dfs(count: 0, arr: [])

 

 

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

BOJ-14503 로봇 청소기 Swift  (0) 2024.04.13
BOJ-1011 Fly me to the Alpha Centauri Swift  (0) 2024.04.13
BOJ-15683 감시 Swift  (1) 2024.04.12
BOJ-10819 차이를 최대로 Swift  (0) 2024.04.12
BOJ-12100 2048(Easy) Swift  (0) 2024.04.12

문제

스타트링크의 사무실은 1×1크기의 정사각형으로 나누어져 있는 N×M 크기의 직사각형으로 나타낼 수 있다. 사무실에는 총 K개의 CCTV가 설치되어져 있는데, CCTV는 5가지 종류가 있다. 각 CCTV가 감시할 수 있는 방법은 다음과 같다.

1번 CCTV는 한 쪽 방향만 감시할 수 있다. 2번과 3번은 두 방향을 감시할 수 있는데, 2번은 감시하는 방향이 서로 반대방향이어야 하고, 3번은 직각 방향이어야 한다. 4번은 세 방향, 5번은 네 방향을 감시할 수 있다.

CCTV는 감시할 수 있는 방향에 있는 칸 전체를 감시할 수 있다. 사무실에는 벽이 있는데, CCTV는 벽을 통과할 수 없다. CCTV가 감시할 수 없는 영역은 사각지대라고 한다.

CCTV는 회전시킬 수 있는데, 회전은 항상 90도 방향으로 해야 하며, 감시하려고 하는 방향이 가로 또는 세로 방향이어야 한다.

CCTV는 CCTV를 통과할 수 있다. 아래 예시를 보자.

0 0 2 0 3
0 6 0 0 0
0 0 6 6 0
0 0 0 0 0

위와 같은 경우에 2의 방향이 ↕ 3의 방향이 ←와 ↓인 경우 감시받는 영역은 다음과 같다.

# # 2 # 3
0 6 # 0 #
0 0 6 6 #
0 0 0 0 #

사무실의 크기와 상태, 그리고 CCTV의 정보가 주어졌을 때, CCTV의 방향을 적절히 정해서, 사각 지대의 최소 크기를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 사무실의 세로 크기 N과 가로 크기 M이 주어진다. (1 ≤ N, M ≤ 8)

둘째 줄부터 N개의 줄에는 사무실 각 칸의 정보가 주어진다. 0은 빈 칸, 6은 벽, 1~5는 CCTV를 나타내고, 문제에서 설명한 CCTV의 종류이다. 

CCTV의 최대 개수는 8개를 넘지 않는다.

출력

첫째 줄에 사각 지대의 최소 크기를 출력한다.

내가 푼 풀이

- 아주 정성스럽게 완전탐색을 하였다.

- 오른쪽 왼쪽 위 아래 탐색함수를 구현해보고 이를 재사용하였고, 각각 cctv의 탐색가능방향을 모두 탐색하고 최솟값을 구했다.

 

import Foundation

// 입력받기
let input = readLine()!.split(separator: " ").map{Int(String($0))!}
let N = input[0], M = input[1]
var office = [[Int]]()
var cctvs = [(serial: Int, x: Int, y: Int)]()
var result = Int.max

// cctv의 위치 저장
for i in 0..<N {
    office.append(readLine()!.split(separator: " ").map{Int(String($0))!})
    for j in 0..<office[i].count {
        if office[i][j] != 0 && office[i][j] != 6 {
            cctvs.append((serial: office[i][j], x: i, y: j))
        }
    }
}
// 모든 경우의 수 탐색
func dfs(count: Int, arr: [[Int]]) {
    if count == cctvs.count {
        result = min(result, sum(arr: arr))
        return
    }
    var cctv = cctvs[count]
    // 1번 cctv의 모든방향 탐색 (4가지)
    if cctv.serial == 1 {
        for i in 0..<4 {
            dfs(count: count+1, arr: serial1Range(direction: i, loc: (x: cctv.x, y: cctv.y), arr: arr))
        }
    }
    // 2번 cctv의 모든방향 탐색 (2가지)
    if cctv.serial == 2 {
        for i in 0..<2 {
            dfs(count: count+1, arr: serial2Range(direction: i, loc: (x: cctv.x, y: cctv.y), arr: arr))
        }
    }
    // 3번 cctv의 모든방향 탐색 (4가지)
    if cctv.serial == 3 {
        for i in 0..<4 {
            dfs(count: count+1, arr: serial3Range(direction: i, loc: (x: cctv.x, y: cctv.y), arr: arr))
        }
    }
    // 4번 cctv의 모든방향 탐색 (4가지)
    if cctv.serial == 4 {
        for i in 0..<4 {
            dfs(count: count+1, arr: serial4Range(direction: i, loc: (x: cctv.x, y: cctv.y), arr: arr))
        }
    }
    // 5번 cctv의 모든방향 탐색 (1가지)
    if cctv.serial == 5 {
        dfs(count: count+1, arr: serial5Range( loc: (x: cctv.x, y: cctv.y), arr: arr))
    }
    
}

// 탐색방향 모두 구현
// 오른쪽
func right(loc: (x: Int, y: Int), arr: [[Int]]) -> [[Int]] {
    var arr = arr
    for i in loc.y+1..<M {
        if arr[loc.x][i] == 6 { break }
        if arr[loc.x][i] == 0 { arr[loc.x][i] = -1 }
    }
    return arr
}
// 위쪽
func up(loc: (x: Int, y: Int), arr: [[Int]]) -> [[Int]] {
    var arr = arr
    for i in (0..<loc.x).reversed() {
        if arr[i][loc.y] == 6 { break }
        if arr[i][loc.y] == 0 { arr[i][loc.y] = -1}
    }
    return arr
}
// 왼쪽
func left(loc: (x: Int, y: Int), arr: [[Int]]) -> [[Int]] {
    var arr = arr
    for i in (0..<loc.y).reversed() {
        if arr[loc.x][i] == 6 { break }
        if arr[loc.x][i] == 0 { arr[loc.x][i] = -1 }
    }
    return arr
}
// 아래쪽
func down(loc: (x: Int, y: Int), arr: [[Int]]) -> [[Int]] {
    var arr = arr
    for i in loc.x+1..<N {
        if arr[i][loc.y] == 6 { break }
        if arr[i][loc.y] == 0 { arr[i][loc.y] = -1}
    }
    return arr
}

// 1번의 탐색
func serial1Range(direction: Int, loc: (x: Int, y: Int), arr: [[Int]]) -> [[Int]] {
    var arr = arr
    // 동
    if direction == 0 {
        return right(loc: loc, arr: arr)
    }
    
    // 서
    if direction == 1 {
        return left(loc: loc, arr: arr)
    }
    
    // 남
    if direction == 2 {
        return down(loc: loc, arr: arr)
    }
    
    // 북
    if direction == 3 {
        return up(loc: loc, arr: arr)
    }
    return arr
}

// 2번의 탐색
func serial2Range(direction: Int, loc: (x: Int, y: Int), arr: [[Int]]) -> [[Int]] {
    var arr = arr
    // 가로
    if direction == 0 {
        arr = right(loc: loc, arr: arr)
        arr = left(loc: loc, arr: arr)
    }
    
    // 세로
    if direction == 1 {
        arr = up(loc: loc, arr: arr)
        arr = down(loc: loc, arr: arr)
    }
    return arr
}

// 3번의 탐색
func serial3Range(direction: Int, loc: (x: Int, y: Int), arr: [[Int]]) -> [[Int]] {
    var arr = arr
    // 위 오
    if direction == 0 {
        arr = up(loc: loc, arr: arr)
        arr = right(loc: loc, arr: arr)
    }
    // 오 아래
    if direction == 1 {
        arr = right(loc: loc, arr: arr)
        arr = down(loc: loc, arr: arr)
    }
    // 아래 왼
    if direction == 2 {
        arr = down(loc: loc, arr: arr)
        arr = left(loc: loc, arr: arr)
    }
    // 왼 위
    if direction == 3 {
        arr = left(loc: loc, arr: arr)
        arr = up(loc: loc, arr: arr)
    }
    return arr
}

// 4번의 탐색
func serial4Range(direction: Int, loc: (x: Int, y: Int), arr: [[Int]]) -> [[Int]] {
    var arr = arr
    // 왼 위 오 (가로 + 위)
    if direction == 0 {
        arr = serial2Range(direction: 0, loc: loc, arr: arr)
        arr = up(loc: loc, arr: arr)
    }
    // 위 오 아래 (세로 + 오)
    if direction == 1 {
        arr = serial2Range(direction: 1, loc: loc, arr: arr)
        arr = right(loc: loc, arr: arr)
    }
    // 오 아래 왼 (가로 + 아래)
    if direction == 2 {
        arr = serial2Range(direction: 0, loc: loc, arr: arr)
        arr = down(loc: loc, arr: arr)
    }
    // 아래 왼 오 (세로 + 왼)
    if direction == 3 {
        arr = serial2Range(direction: 1, loc: loc, arr: arr)
        arr = left(loc: loc, arr: arr)
    }
    return arr
}

// 5번의 탐색
func serial5Range(loc: (x: Int, y: Int), arr: [[Int]]) -> [[Int]] {
    var arr = arr
    arr = serial2Range(direction: 0, loc: loc, arr: arr)
    arr = serial2Range(direction: 1, loc: loc, arr: arr)
    return arr
}

// 사각지대 계산
func sum(arr: [[Int]]) -> Int{
    var sum = 0
    for i in 0..<arr.count {
        sum += arr[i].filter{ $0 == 0}.count
    }
    return sum
}

dfs(count: 0, arr: office)
print(result)

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

BOJ-1011 Fly me to the Alpha Centauri Swift  (0) 2024.04.13
BOJ-10974 모든 순열 Swift  (0) 2024.04.13
BOJ-10819 차이를 최대로 Swift  (0) 2024.04.12
BOJ-12100 2048(Easy) Swift  (0) 2024.04.12
BOJ-2003 수들의 합 2 Swift  (0) 2024.04.11

문제

N개의 정수로 이루어진 배열 A가 주어진다. 이때, 배열에 들어있는 정수의 순서를 적절히 바꿔서 다음 식의 최댓값을 구하는 프로그램을 작성하시오.

|A[0] - A[1]| + |A[1] - A[2]| + ... + |A[N-2] - A[N-1]|

입력

첫째 줄에 N (3 ≤ N ≤ 8)이 주어진다. 둘째 줄에는 배열 A에 들어있는 정수가 주어진다. 배열에 들어있는 정수는 -100보다 크거나 같고, 100보다 작거나 같다.

출력

첫째 줄에 배열에 들어있는 수의 순서를 적절히 바꿔서 얻을 수 있는 식의 최댓값을 출력한다.

내가 푼 풀이

- 백트래킹을 이용해서 주어진 A배열 순서의 모든 경우의수를 구해서 최대값을 갱신한다.

 

import Foundation

//입력받기
let N = Int(readLine()!)!
var arr = readLine()!.split(separator: " ").map{Int(String($0))!}
var result = 0
var visited = Array(repeating: false, count: N)

// DFS 백트래킹
func dfs(count: Int, nums: [Int]) {
    if count == N {
        result = max(result, sum(nums: nums))
        return
    }
    
    for i in 0..<N {
        if !visited[i] {
            visited[i] = true
            dfs(count: count+1, nums: nums + [arr[i]])
            visited[i] = false
        }
    }
}

// 합산
func sum(nums: [Int]) -> Int{
    var sum = 0
    for i in 0..<nums.count-1 {
        sum += abs(nums[i] - nums[i+1])
    }
    return sum
}

dfs(count: 0, nums: [])
print(result)

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

BOJ-10974 모든 순열 Swift  (0) 2024.04.13
BOJ-15683 감시 Swift  (1) 2024.04.12
BOJ-12100 2048(Easy) Swift  (0) 2024.04.12
BOJ-2003 수들의 합 2 Swift  (0) 2024.04.11
BOJ-1107 리모컨 Swift  (0) 2024.04.11

+ Recent posts