문제

2048 게임은 4×4 크기의 보드에서 혼자 즐기는 재미있는 게임이다. 이 링크를 누르면 게임을 해볼 수 있다.

이 게임에서 한 번의 이동은 보드 위에 있는 전체 블록을 상하좌우 네 방향 중 하나로 이동시키는 것이다. 이때, 같은 값을 갖는 두 블록이 충돌하면 두 블록은 하나로 합쳐지게 된다. 한 번의 이동에서 이미 합쳐진 블록은 또 다른 블록과 다시 합쳐질 수 없다. (실제 게임에서는 이동을 한 번 할 때마다 블록이 추가되지만, 이 문제에서 블록이 추가되는 경우는 없다)

<그림 1>의 경우에서 위로 블록을 이동시키면 <그림 2>의 상태가 된다. 여기서, 왼쪽으로 블록을 이동시키면 <그림 3>의 상태가 된다.

<그림 4>의 상태에서 블록을 오른쪽으로 이동시키면 <그림 5>가 되고, 여기서 다시 위로 블록을 이동시키면 <그림 6>이 된다. 여기서 오른쪽으로 블록을 이동시켜 <그림 7>을 만들 수 있다.

 

이 문제에서 다루는 2048 게임은 보드의 크기가 N×N 이다. 보드의 크기와 보드판의 블록 상태가 주어졌을 때, 최대 5번 이동해서 만들 수 있는 가장 큰 블록의 값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 보드의 크기 N (1 ≤ N ≤ 20)이 주어진다. 둘째 줄부터 N개의 줄에는 게임판의 초기 상태가 주어진다. 0은 빈 칸을 나타내며, 이외의 값은 모두 블록을 나타낸다. 블록에 쓰여 있는 수는 2보다 크거나 같고, 1024보다 작거나 같은 2의 제곱꼴이다. 블록은 적어도 하나 주어진다.

출력

최대 5번 이동시켜서 얻을 수 있는 가장 큰 블록을 출력한다.

내가 푼 풀이

- 다른 알고리즘 풀이방법이 떠오르지않아 위 아래 왼쪽 오른쪽 방향으로 이동하는 동작을 구현해서 모든 경우의수를 탐색했다.

- 최대 5번 움직일 수 있으니, 동작을 구현해서 5번 굴려줬다.

 

import Foundation

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

// 움직이기
func move(count: Int, arr: [[Int]]) {
    // 5번 움직였다면 최댓값 구하고 끝내기
    if count == 5 {
        for i in 0..<N {
            result = max(result, arr[i].max()!)
        }
        return
    }
    
    //UP 위로이동
    var board = arr
    for i in 0..<N {
        var insert = [Int]()
        for j in 0..<N {
            // 0이 아닌값들을 저장
            if board[j][i] != 0 {
                insert.append(board[j][i])
                board[j][i] = 0
            }
        }
        // 모든값이 0이라면 insert에 저장되지않았으므로 패스
        if !insert.isEmpty {
            var index = 0
            var arrIndex = 0
            // 같은값이면 더해서 가장 위로 올려주기
            while arrIndex < insert.count - 1 {
                if insert[arrIndex] == insert[arrIndex+1] {
                    board[index][i] = insert[arrIndex] * 2
                    arrIndex += 2
                } else {
                    board[index][i] = insert[arrIndex]
                    arrIndex += 1
                }
                index += 1
            }
            // 맨 마지막번째 수가 합쳐지지않았다면 마지막번에 저장
            if arrIndex < insert.count {
                board[index][i] = insert[arrIndex]
            }
        }
    }
    // 위로 이동 후 이동한 보드를 그대로 다른방향으로 이동
    move(count: count+1, arr: board)
    
    //DOWN 아래로 이동
    board = arr
    for i in 0..<N {
        var insert = [Int]()
        for j in (0..<N).reversed() {
            if board[j][i] != 0 {
                insert.append(board[j][i])
                board[j][i] = 0
            }
        }
        if !insert.isEmpty {
            var index = N-1
            var arrIndex = 0
            while arrIndex < insert.count - 1 {
                if insert[arrIndex] == insert[arrIndex+1] {
                    board[index][i] = insert[arrIndex] * 2
                    arrIndex += 2
                } else {
                    board[index][i] = insert[arrIndex]
                    arrIndex += 1
                }
                index -= 1
            }
            if arrIndex < insert.count {
                board[index][i] = insert[arrIndex]
            }
        }
    }
    // 아래로 이동후 다른방향으로 이동
    move(count: count+1, arr: board)
    
    //LEFT 왼쪽 이동
    board = arr
    for i in 0..<N {
        var insert = [Int]()
        for j in 0..<N {
            if board[i][j] != 0 {
                insert.append(board[i][j])
                board[i][j] = 0
            }
        }
        if !insert.isEmpty {
            var index = 0
            var arrIndex = 0
            while arrIndex < insert.count - 1 {
                if insert[arrIndex] == insert[arrIndex+1] {
                    board[i][index] = insert[arrIndex] * 2
                    arrIndex += 2
                } else {
                    board[i][index] = insert[arrIndex]
                    arrIndex += 1
                }
                index += 1
            }
            if arrIndex < insert.count {
                board[i][index] = insert[arrIndex]
            }
        }
    }
    // 왼쪽으로 이동후 다른방향으로 이동
    move(count: count+1, arr: board)
    
    //RIGHT 오른쪽 이동
    board = arr
    for i in 0..<N {
        var insert = [Int]()
        for j in (0..<N).reversed() {
            if board[i][j] != 0 {
                insert.append(board[i][j])
                board[i][j] = 0
            }
        }
        if !insert.isEmpty {
            var index = N-1
            var arrIndex = 0
            while arrIndex < insert.count - 1 {
                if insert[arrIndex] == insert[arrIndex+1] {
                    board[i][index] = insert[arrIndex] * 2
                    arrIndex += 2
                } else {
                    board[i][index] = insert[arrIndex]
                    arrIndex += 1
                }
                index -= 1
            }
            if arrIndex < insert.count {
                board[i][index] = insert[arrIndex]
            }
        }
    }
    // 오른쪽으로 이동 후 다른방향으로 이동
    move(count: count+1, arr: board)
}

move(count: 0, arr: arr)
print(result)

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

BOJ-15683 감시 Swift  (1) 2024.04.12
BOJ-10819 차이를 최대로 Swift  (0) 2024.04.12
BOJ-2003 수들의 합 2 Swift  (0) 2024.04.11
BOJ-1107 리모컨 Swift  (0) 2024.04.11
BOJ-1476 날짜 계산 Swift  (0) 2024.04.11

문제

N개의 수로 된 수열 A[1], A[2], …, A[N] 이 있다. 이 수열의 i번째 수부터 j번째 수까지의 합 A[i] + A[i+1] + … + A[j-1] + A[j]가 M이 되는 경우의 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N(1 ≤ N ≤ 10,000), M(1 ≤ M ≤ 300,000,000)이 주어진다. 다음 줄에는 A[1], A[2], …, A[N]이 공백으로 분리되어 주어진다. 각각의 A[x]는 30,000을 넘지 않는 자연수이다.

출력

첫째 줄에 경우의 수를 출력한다.

내가 푼 풀이

- 수열은 자연수로 이루어져있고, 연속된 수열의 합이 M이 되는 경우의 수를 구한다.

- 재귀호출로 수열의 첫번째부터 마지막번째까지 연속나열된 값들과 더하여 구한다.

 

import Foundation

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

// 더하는 함수
func sum(index: Int, total: Int) {
    if total == M {
        count += 1
        return
    }
    if total > M {
        return
    }
    if index+1 < A.count {
        sum(index: index+1, total: total + A[index+1])
    } else {
        return
    }
}

for i in 0..<A.count {
    sum(index: i, total: A[i])
}
print(count)

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

BOJ-10819 차이를 최대로 Swift  (0) 2024.04.12
BOJ-12100 2048(Easy) Swift  (0) 2024.04.12
BOJ-1107 리모컨 Swift  (0) 2024.04.11
BOJ-1476 날짜 계산 Swift  (0) 2024.04.11
BOJ-14500 테트로미노 Swift (Brute-force)  (0) 2024.04.11

문제

수빈이는 TV를 보고 있다. 수빈이는 채널을 돌리려고 했지만, 버튼을 너무 세게 누르는 바람에, 일부 숫자 버튼이 고장났다.

리모컨에는 버튼이 0부터 9까지 숫자, +와 -가 있다. +를 누르면 현재 보고있는 채널에서 +1된 채널로 이동하고, -를 누르면 -1된 채널로 이동한다. 채널 0에서 -를 누른 경우에는 채널이 변하지 않고, 채널은 무한대 만큼 있다.

수빈이가 지금 이동하려고 하는 채널은 N이다. 어떤 버튼이 고장났는지 주어졌을 때, 채널 N으로 이동하기 위해서 버튼을 최소 몇 번 눌러야하는지 구하는 프로그램을 작성하시오.

수빈이가 지금 보고 있는 채널은 100번이다.

입력

첫째 줄에 수빈이가 이동하려고 하는 채널 N (0 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 고장난 버튼의 개수 M (0 ≤ M ≤ 10)이 주어진다. 고장난 버튼이 있는 경우에는 셋째 줄에는 고장난 버튼이 주어지며, 같은 버튼이 여러 번 주어지는 경우는 없다.

 

출력

첫째 줄에 채널 N으로 이동하기 위해 버튼을 최소 몇 번 눌러야 하는지를 출력한다.

내가 푼 풀이

-처음 문제를 접했을때, 문제를 어떻게 풀까에 대한 해답이 쉽게 나오지않았다.

-가장 오래걸리는 완전탐색을 배제하고 더 빠른 탐색방법이 없을까 dp? dfs? 여러가지 구성해보다가 결국엔 다른 사람의 풀이 도움을 받아완전탐색으로 풀었다.

-완전탐색도 모든 경우의 수를 탐색해야하므로 탐색기준과 범위를 정하기가 쉽지않았다.

-여기서 완전탐색으로 푼 방법은 아래와 같다.

 

N번의 채널로 이동하는방법

1. 고장나지않은 번호를 입력해 이동한다.

2. +/-버튼을 이용해 한칸씩 이동한다.

3. 고장나지않은 버튼을 이용해 N번채널과 가까운채널로 이동 후 2번방법으로 이동한다.

 

N의 최댓값은 50만이지만 채널은 무한대이다.

현재 채널 100을 기준으로 50만채널로 이동할 때, 단순히 +/-로 이동한다면

100 -> 500,000 보다 990,000 -> 500,000 이동이 훨씬 적게 누른다.

 

100 채널에서 500,000로 이동하려면 +/- 몇번을 눌러야할까

바로 |500,000 - 100|번 눌러야한다.

마찬가지로 990,000에서 500,000채널로 이동하려면 |500,000 - 990,000| 번 누르면 된다.

 

결국 우리가 이동해야할 N 채널은 50만까지 정해져있지만,

최소로 이동하려면 우리는 100만까지 범위의 채널에서 이동해서 최솟값을 찾는게 해답이 될 것이다.

 

이것을 이렇게 생각해냈다.

채널 0부터 100만까지 고장나지 않은 번호로 이동을 했다면,

채널을 이동할때의 누른 횟수와 이동한 채널에서 N채널까지 +/-이동만으로 누른 횟수를 더한다.

더한 값들의 최솟값은 문제에서 구하는 최솟값이 될 것이다.

 

이렇게 생각한것도 다른사람의 풀이 도움을 받아서 생각해낼 수 있었다.

너무 어려웠다

import Foundation

// 입력받기
let N = Int(readLine()!)!
let M = Int(readLine()!)!
var brokenBtns = [Int]()
if M != 0 {
    brokenBtns = readLine()!.split(separator: " ").map{Int(String($0))!}
}
// 현재채널 100, 100에서 누른횟수 최솟값
var minCount = abs(N - 100)

// 100만채널까지 모든 경우의 수를 탐색한다.
for i in 0..<1000001 {
    let len = length(num: i)
    // 고장나지않은 번호로 이동했다면 이동할때 누른 횟수와, 이동한 채널에서 +/-로 이동한 횟수를 더한다.
    if len > 0 {
        let count = abs(N - i)
        minCount = min(minCount, count + len)
    }
}

// 고장나지 않은 번호로 이동했을때 버튼을 누른 횟수를 구한다.
func length(num: Int) -> Int{
    var n = num
    // 목적지 N이 0채널이라면
    if n == 0 {
        // 0번이 고장났다면 번호눌러서 이동할 수 없다.
        // 1번에서 이동해야함.
        if brokenBtns.contains(n)    {
            return 0
        } else {
            return 1
        }
    }
    
    var len = 0
    // 누른횟수 계산
    // 이동하려는 채널에 고장난번호가 포함되있다면 애초에 이동이 불가능하므로 0 리턴
    while n > 0 {
        if brokenBtns.contains(n % 10) { return 0 }
        n = n / 10
        len += 1
    }
    return len
}


print(minCount)

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

BOJ-12100 2048(Easy) Swift  (0) 2024.04.12
BOJ-2003 수들의 합 2 Swift  (0) 2024.04.11
BOJ-1476 날짜 계산 Swift  (0) 2024.04.11
BOJ-14500 테트로미노 Swift (Brute-force)  (0) 2024.04.11
BOJ-1759 암호 만들기 Swift  (0) 2024.04.10

문제

준규가 사는 나라는 우리가 사용하는 연도와 다른 방식을 이용한다. 준규가 사는 나라에서는 수 3개를 이용해서 연도를 나타낸다. 각각의 수는 지구, 태양, 그리고 달을 나타낸다.

지구를 나타내는 수를 E, 태양을 나타내는 수를 S, 달을 나타내는 수를 M이라고 했을 때, 이 세 수는 서로 다른 범위를 가진다. (1 ≤ E ≤ 15, 1 ≤ S ≤ 28, 1 ≤ M ≤ 19)

우리가 알고있는 1년은 준규가 살고있는 나라에서는 1 1 1로 나타낼 수 있다. 1년이 지날 때마다, 세 수는 모두 1씩 증가한다. 만약, 어떤 수가 범위를 넘어가는 경우에는 1이 된다.

예를 들어, 15년은 15 15 15로 나타낼 수 있다. 하지만, 1년이 지나서 16년이 되면 16 16 16이 아니라 1 16 16이 된다. 이유는 1 ≤ E ≤ 15 라서 범위를 넘어가기 때문이다.

E, S, M이 주어졌고, 1년이 준규가 사는 나라에서 1 1 1일때, 준규가 사는 나라에서 E S M이 우리가 알고 있는 연도로 몇 년인지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 세 수 E, S, M이 주어진다. 문제에 나와있는 범위를 지키는 입력만 주어진다.

출력

첫째 줄에 E S M으로 표시되는 가장 빠른 연도를 출력한다. 1 1 1은 항상 1이기 때문에, 정답이 음수가 나오는 경우는 없다.

내가 푼 풀이

- 주어진 E, S, M을 1, 1, 1 부터 같아질때까지 더한다.

- 더한 수를 출력

 

import Foundation

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

while !(arr == input) {
    arr = arr.map{$0 + 1}
    result += 1
    if arr[0] > 15 {
        arr[0] = 1
    }
    if arr[1] > 28 {
        arr[1] = 1
    }
    if arr[2] > 19 {
        arr[2] = 1
    }
}

print(result)

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

BOJ-2003 수들의 합 2 Swift  (0) 2024.04.11
BOJ-1107 리모컨 Swift  (0) 2024.04.11
BOJ-14500 테트로미노 Swift (Brute-force)  (0) 2024.04.11
BOJ-1759 암호 만들기 Swift  (0) 2024.04.10
BOJ-1182 부분수열의 합 Swift  (0) 2024.04.10

문제

폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다.

  • 정사각형은 서로 겹치면 안 된다.
  • 도형은 모두 연결되어 있어야 한다.
  • 정사각형의 변끼리 연결되어 있어야 한다. 즉, 꼭짓점과 꼭짓점만 맞닿아 있으면 안 된다.

정사각형 4개를 이어 붙인 폴리오미노는 테트로미노라고 하며, 다음과 같은 5가지가 있다.

아름이는 크기가 N×M인 종이 위에 테트로미노 하나를 놓으려고 한다. 종이는 1×1 크기의 칸으로 나누어져 있으며, 각각의 칸에는 정수가 하나 쓰여 있다.

테트로미노 하나를 적절히 놓아서 테트로미노가 놓인 칸에 쓰여 있는 수들의 합을 최대로 하는 프로그램을 작성하시오.

테트로미노는 반드시 한 정사각형이 정확히 하나의 칸을 포함하도록 놓아야 하며, 회전이나 대칭을 시켜도 된다.

입력

첫째 줄에 종이의 세로 크기 N과 가로 크기 M이 주어진다. (4 ≤ N, M ≤ 500)

둘째 줄부터 N개의 줄에 종이에 쓰여 있는 수가 주어진다. i번째 줄의 j번째 수는 위에서부터 i번째 칸, 왼쪽에서부터 j번째 칸에 쓰여 있는 수이다. 입력으로 주어지는 수는 1,000을 넘지 않는 자연수이다.

출력

첫째 줄에 테트로미노가 놓인 칸에 쓰인 수들의 합의 최댓값을 출력한다.

내가 푼 풀이

- 주어진 NxM종이에 테트로미노 하나 놓았을때의 합산 최댓값을 구하는 문제다.

- 테트로미노는 회전 및 대칭이 가능하다.

- 다른 특별한 규칙을 찾을 수 없어서 모든 경우의수를 찾는 방법을 택했다.

- 테트로미노를 회전과 대칭했을땐 규칙이 어느정도 보였고, 이를 구현했다.

- 배열을 순회하면서 모든 경우의수를 구하기위해 각 테트로미노에서 기준점을 정하고, 그 점을 기준으로 회전 및 대칭하였다.

 

기준점: X표시

1) 첫번째 테트로미노

첫번째 테트로미노는 대칭한 모습은 같고, 회전을 하면 4가지의 모형이 나온다.

하지만 배열을 순회하면서 최댓값을 구할때, 기준점 (0,0)과 기준점(0,3)의 180도 회전한 모습은 같은 최댓값을 갖는다.

따라서 첫번째 테트로미노는 두가지(0도, 90도) 모형으로 최댓값을 구한다.

 

 

2) 두번째 테트로미노

두번째 테트로미노는 대칭과 회전을해도 똑같은 모형이 나온다.

회전을 하면 기준점이 달라지지만, 이는 역시 배열 순회하면서 구한값을 다시 구하게 되므로 위와 같은 모형으로 구한다.

두번째 테트로미노는 한가지 모형으로 최댓값을 구한다.

 

 

3) 세번째 테트로미노

세번째 테트로미노는 회전 및 대칭하였을때 8가지의 모형이 나온다.

이 테트로미노는 배열을 순회하면서 모두 다른값이 나오므로 8가지 모형으로 최댓값을 구한다.

 

4) 네번째 테트로미노

네번째 테트로미노는 회전 및 대칭하였을때 4가지의 모형이 나온다.

이 4가지모형으로 최댓값을 구한다.

 

5) 다섯번째 테트로미노

다섯번째 테트로미노는 회전및 대칭해도 4가지의 모형이 나온다.

이 4가지모형으로 최댓값을 구한다.

 

위 모든 모형이 나올 수 있는 경우의수를 기준점을 기준으로 구한다.

 

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

 

import Foundation

// 입력
let input = readLine()!.split(separator: " ").map{Int(String($0))!}
let N = input[0], M = input[1]
var paper = [[Int]]()
var maxNum = Int.min
for i in 0..<N {
    paper.append(readLine()!.split(separator: " ").map{Int(String($0))!})
}

// 첫번째 테트로미노 경우의수
func poly1(x: Int, y: Int) {
    var sum = 0
    if x+3 >= M && y+3 >= N { return }
    if x+3 >= M {
        sum = paper[y][x] + paper[y+1][x] + paper[y+2][x] + paper[y+3][x]
        maxNum = max(maxNum, sum)
    } else if y+3 >= N {
        sum = paper[y][x] + paper[y][x+1] + paper[y][x+2] + paper[y][x+3]
        maxNum = max(maxNum, sum)
    } else {
        sum = paper[y][x] + paper[y+1][x] + paper[y+2][x] + paper[y+3][x]
        maxNum = max(maxNum, sum)
        sum = paper[y][x] + paper[y][x+1] + paper[y][x+2] + paper[y][x+3]
        maxNum = max(maxNum, sum)
    }
}

// 두번째 테트로미노 경우의수
func poly2(x: Int, y: Int) {
    var sum = 0
    if x+1 >= M || y+1 >= N { return }
    sum = paper[y][x] + paper[y+1][x] + paper[y][x+1] + paper[y+1][x+1]
    maxNum = max(maxNum, sum)
}

// 세번째 테트로미노 경우의수
func poly3(x: Int, y: Int) {
    var sum = 0
    if y-2 >= 0 {
        sum = paper[y][x] + paper[y-1][x] + paper[y-2][x]
        if x+1 < M {
            maxNum = max(maxNum, sum + paper[y][x+1])
        }
        if x-1 >= 0 {
            maxNum = max(maxNum, sum + paper[y][x-1])
        }
    }
    if x+2 < M {
        sum = paper[y][x] + paper[y][x+1] + paper[y][x+2]
        if y+1 < N {
            maxNum = max(maxNum, sum + paper[y+1][x])
        }
        if y-1 >= 0 {
            maxNum = max(maxNum, sum + paper[y-1][x])
        }
    }
    if y+2 < N {
        sum = paper[y][x] + paper[y+1][x] + paper[y+2][x]
        if x+1 < M {
            maxNum = max(maxNum, sum + paper[y][x+1])
        }
        if x-1 >= 0 {
            maxNum = max(maxNum, sum + paper[y][x-1])
        }
    }
    if x-2 >= 0 {
        sum = paper[y][x] + paper[y][x-1] + paper[y][x-2]
        if y+1 < N {
            maxNum = max(maxNum, sum + paper[y+1][x])
        }
        if y-1 >= 0 {
            maxNum = max(maxNum, sum + paper[y-1][x])
        }
    }
}

// 네번째 테트로미노 경우의수
func poly4(x: Int, y: Int) {
    var sum = 0
    if y-1 >= 0 {
        sum = paper[y][x] + paper[y-1][x]
        if x+1 < M && y+1 < N {
            maxNum = max(maxNum, sum + paper[y][x+1] + paper[y+1][x+1])
        }
        if x-1 >= 0 && y+1 < N {
            maxNum = max(maxNum, sum + paper[y][x-1] + paper[y+1][x-1])
        }
    }
    if x+1 < M {
        sum = paper[y][x] + paper[y][x+1]
        if y+1 < N && x-1 >= 0 {
            maxNum = max(maxNum, sum + paper[y+1][x] + paper[y+1][x-1])
        }
        if y-1 >= 0 && x-1 >= 0 {
            maxNum = max(maxNum, sum + paper[y-1][x] + paper[y-1][x-1])
        }
    }
}

// 다섯번째 테트로미노 경우의수
func poly5(x: Int, y:Int) {
    var sum = 0
    if x-1 >= 0 && x+1 < M {
        sum = paper[y][x-1] + paper[y][x] + paper[y][x+1]
        if y+1 < N {
            maxNum = max(maxNum, sum + paper[y+1][x])
        }
        if y-1 >= 0 {
            maxNum = max(maxNum, sum + paper[y-1][x])
        }
    }
    if y-1 >= 0 && y+1 < N {
        sum = paper[y-1][x] + paper[y][x] + paper[y+1][x]
        if x+1 < M {
            maxNum = max(maxNum, sum + paper[y][x+1])
        }
        if x-1 >= 0 {
            maxNum = max(maxNum, sum + paper[y][x-1])
        }
    }
}

// 주어진 종이에서 기준점을 기준으로 모든 테트로미노 모형 경우의수를 따지고 최댓값을 구한다.
for i in 0..<N {
    for j in 0..<M {
        poly1(x: j, y: i)
        poly2(x: j, y: i)
        poly3(x: j, y: i)
        poly4(x: j, y: i)
        poly5(x: j, y: i)
    }
}

print(maxNum)

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

BOJ-1107 리모컨 Swift  (0) 2024.04.11
BOJ-1476 날짜 계산 Swift  (0) 2024.04.11
BOJ-1759 암호 만들기 Swift  (0) 2024.04.10
BOJ-1182 부분수열의 합 Swift  (0) 2024.04.10
BOJ-15686 치킨 배달 Swift  (0) 2024.04.10

문제

바로 어제 최백준 조교가 방 열쇠를 주머니에 넣은 채 깜빡하고 서울로 가 버리는 황당한 상황에 직면한 조교들은, 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

+ Recent posts