문제

여행을 떠난 세준이는 지도를 하나 구하였다. 이 지도는 아래 그림과 같이 직사각형 모양이며 여러 칸으로 나뉘어져 있다. 한 칸은 한 지점을 나타내는데 각 칸에는 그 지점의 높이가 쓰여 있으며, 각 지점 사이의 이동은 지도에서 상하좌우 이웃한 곳끼리만 가능하다.

현재 제일 왼쪽 위 칸이 나타내는 지점에 있는 세준이는 제일 오른쪽 아래 칸이 나타내는 지점으로 가려고 한다. 그런데 가능한 힘을 적게 들이고 싶어 항상 높이가 더 낮은 지점으로만 이동하여 목표 지점까지 가고자 한다. 위와 같은 지도에서는 다음과 같은 세 가지 경로가 가능하다.

  

지도가 주어질 때 이와 같이 제일 왼쪽 위 지점에서 출발하여 제일 오른쪽 아래 지점까지 항상 내리막길로만 이동하는 경로의 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 지도의 세로의 크기 M과 가로의 크기 N이 빈칸을 사이에 두고 주어진다. 이어 다음 M개 줄에 걸쳐 한 줄에 N개씩 위에서부터 차례로 각 지점의 높이가 빈 칸을 사이에 두고 주어진다. M과 N은 각각 500이하의 자연수이고, 각 지점의 높이는 10000이하의 자연수이다.

출력

첫째 줄에 이동 가능한 경로의 수 H를 출력한다. 모든 입력에 대하여 H는 10억 이하의 음이 아닌 정수이다.

내가 푼 풀이

- dp테이블을 주어진 지도의 크기만큼 만든다.

- 한 지점을 선택했을때, 상하좌우보다 작은 값이라면, 이동할 수 있는 곳이다.

- 처음에는 위 조건대로 이중 반복문을 돌아 dp에 값을 입력하려고 했다.

- 반복문의 특성상 0행을 모두 돌고, 1행을 모두 돌고.... m행을 돌다 보니 위 예제그림 3번째처럼 왼쪽으로 이동하는 경우는 dp에 저장되지 않았다.

- 위 문제는 dfs + dp를 이용해 야했던 것이다.

- dfs로 0,0 지점에서 m, n지점으로 이동한 경우 해당 경로들의 dp값을 모두 1로 입력한다.

- 다른 갈래로 갔던 지점이 dp의 값이 1인경우 1 반환

 

 

import Foundation

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

// 지도를 배열로 입력
for i in 0..<input[0] {
    arr.append(readLine()!.split(separator: " ").map{ Int($0)! })
}

// DFS 재귀호출
// 상하좌우 방향이 현재위치보다 작은경우 이동가능한 곳이다.
// dp의 초기값은 -1이므로 새롭게 가는곳이다.
func dfs(_ x: Int, _ y: Int) -> Int {
    // 목적지에 도착하면 1반환
    if x == input[1] - 1 && y == input[0] - 1 {
        return 1
    }
    // 처음가보는곳이라면 해당위치의 상하좌우방향중 이동할 수 있는 곳으로 재귀호출
    if dp[y][x] == -1 {
        dp[y][x] = 0
        for i in 0..<4 {
            var ny = y + dy[i]
            var nx = x + dx[i]

            if (ny >= 0 && ny < input[0]) && (nx >= 0 && nx < input[1] ) {
                if arr[ny][nx] < arr[y][x] {
                    dp[y][x] += dfs(nx, ny)
                }
            }
        }
    }
    // 이미 이동했던곳이라면 저장된 DP값을 반환
    return dp[y][x]
}

print(dfs(0, 0))

 

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

BOJ-2212 센서 Swift  (0) 2023.05.30
BOJ-2133 타일 채우기 Swift  (0) 2023.05.30
BOJ-1012 유기농 배추 Swift  (0) 2023.05.27
BOJ-1343 폴리오미노 Swift  (0) 2023.05.27
BOJ-11722 가장 긴 감소하는 부분 수열 Swift  (0) 2023.05.27

문제

차세대 영농인 한나는 강원도 고랭지에서 유기농 배추를 재배하기로 하였다. 농약을 쓰지 않고 배추를 재배하려면 배추를 해충으로부터 보호하는 것이 중요하기 때문에, 한나는 해충 방지에 효과적인 배추흰지렁이를 구입하기로 결심한다. 이 지렁이는 배추근처에 서식하며 해충을 잡아 먹음으로써 배추를 보호한다. 특히, 어떤 배추에 배추흰지렁이가 한 마리라도 살고 있으면 이 지렁이는 인접한 다른 배추로 이동할 수 있어, 그 배추들 역시 해충으로부터 보호받을 수 있다. 한 배추의 상하좌우 네 방향에 다른 배추가 위치한 경우에 서로 인접해있는 것이다.

한나가 배추를 재배하는 땅은 고르지 못해서 배추를 군데군데 심어 놓았다. 배추들이 모여있는 곳에는 배추흰지렁이가 한 마리만 있으면 되므로 서로 인접해있는 배추들이 몇 군데에 퍼져있는지 조사하면 총 몇 마리의 지렁이가 필요한지 알 수 있다. 예를 들어 배추밭이 아래와 같이 구성되어 있으면 최소 5마리의 배추흰지렁이가 필요하다. 0은 배추가 심어져 있지 않은 땅이고, 1은 배추가 심어져 있는 땅을 나타낸다.

1 1 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0
0 0 1 1 0 0 0 1 1 1
0 0 0 0 1 0 0 1 1 1

입력

입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트 케이스에 대해 첫째 줄에는 배추를 심은 배추밭의 가로길이 M(1 ≤ M ≤ 50)과 세로길이 N(1 ≤ N ≤ 50), 그리고 배추가 심어져 있는 위치의 개수 K(1 ≤ K ≤ 2500)이 주어진다. 그다음 K 줄에는 배추의 위치 X(0 ≤ X ≤ M-1), Y(0 ≤ Y ≤ N-1)가 주어진다. 두 배추의 위치가 같은 경우는 없다.

출력

각 테스트 케이스에 대해 필요한 최소의 배추흰지렁이 마리 수를 출력한다.

내가 푼 풀이

- 주어진 땅을 배열로 입력받고, 인접한 인덱스의 연결리스트를 입력한다.

- 연결된 배추들을 탐색하고, 탐색이 완료된곳은 지렁이 한 마리가 필요하므로, 한 마리 카운트해 준다.

- 인접된 배추들을 탐색할 때, 순서가 중요하지 않으므로 BFS, DFS 둘 다 사용가능하다.

 

import Foundation

let count = Int(readLine()!)!

for i in 0..<count {
    var input = readLine()!.split(separator: " ").map{ Int($0)! }
    var arr = Array(repeating: Array(repeating: 0, count: input[0]), count: input[1])
    var graph = [[Int]: [[Int]]]()
    var result = 0
    
    // 주어진 땅을 배열로 입력
    for j in 0..<input[2] {
        var idx = readLine()!.split(separator: " ").map{ Int($0)! }
        arr[idx[1]][idx[0]] = 1
    }
    
    // 땅에 심어진 배추와 인접한 배추들의 위치를 연결리스트로 입력
    // [[x,y] : [[인접한 배추들의 위치들]]
    // 인접한 배추가 없는경우 자신의 위치 한곳에도 지렁이가 필요하므로 자신의 위치도 입력한다.
    for j in 0..<arr.count {
        for k in 0..<arr[0].count {
            if arr[j][k] == 0 { continue }
            if graph[[j,k]] == nil {
                graph[[j,k]] = [[j,k]]
            } else {
                graph[[j,k]]!.append([j,k])
            }
            if j > 0 && arr[j-1][k] == 1 {
                if !graph[[j,k]]!.contains([j-1, k]) {
                    graph[[j,k]]!.append([j-1,k])
                }
                if graph[[j-1,k]] == nil {
                    graph[[j-1,k]] = [[j,k]]
                } else {
                    if !graph[[j-1,k]]!.contains([j,k]) {
                        graph[[j-1,k]]!.append([j,k])
                    }
                }
            }
            if j < arr.count-1 && arr[j+1][k] == 1 {
                if !graph[[j,k]]!.contains([j+1, k]) {
                    graph[[j,k]]!.append([j+1,k])
                }
                if graph[[j+1,k]] == nil {
                    graph[[j+1,k]] = [[j,k]]
                } else {
                    if !graph[[j+1,k]]!.contains([j,k]) {
                        graph[[j+1,k]]!.append([j,k])
                    }
                }
            }
            if k > 0 && arr[j][k-1] == 1 {
                if !graph[[j,k]]!.contains([j, k-1]) {
                    graph[[j,k]]!.append([j,k-1])
                }
                if graph[[j,k-1]] == nil {
                    graph[[j,k-1]] = [[j,k]]
                } else {
                    if !graph[[j,k-1]]!.contains([j,k]) {
                        graph[[j,k-1]]!.append([j,k])
                    }
                }
            }
            if k < arr[0].count-1 && arr[j][k+1] == 1 {
                if !graph[[j,k]]!.contains([j, k+1]) {
                    graph[[j,k]]!.append([j,k+1])
                }
                if graph[[j,k+1]] == nil {
                    graph[[j,k+1]] = [[j,k]]
                } else {
                    if !graph[[j,k+1]]!.contains([j,k]) {
                        graph[[j,k+1]]!.append([j,k])
                    }
                }
            }
        }
    }
    
    // 인접한 배추들을 탐색하는데 순서가 중요하지 않으므로 BFS, DFS 둘다 사용가능하다.
    // 여기서는 BFS를 사용했다.
    var totalVisitedQueue = [[Int]]()
    var visitedQueue = [[Int]]()
    var needVisitQueue = [[Int]]()
    
    for j in 0..<arr.count {
        for k in 0..<arr[0].count {
            if arr[j][k] == 1 && !totalVisitedQueue.contains([j,k]) {
                needVisitQueue = [[j,k]]
                
                while !needVisitQueue.isEmpty {
                    var node = needVisitQueue.removeFirst()
                    if visitedQueue.contains(node) { continue }
                    visitedQueue.append(node)
                    
                    needVisitQueue += graph[node]!
                }
                totalVisitedQueue += visitedQueue
                result += 1
                visitedQueue.removeAll()
            }
        }
    }
    print(result)
    
}

문제

민식이는 다음과 같은 폴리오미노 2개를 무한개만큼 가지고 있다. AAAA와 BB

이제 '.'와 'X'로 이루어진 보드판이 주어졌을 때, 민식이는 겹침없이 'X'를 모두 폴리오미노로 덮으려고 한다. 이때, '.'는 폴리오미노로 덮으면 안 된다.

폴리오미노로 모두 덮은 보드판을 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 보드판이 주어진다. 보드판의 크기는 최대 50이다.

출력

첫째 줄에 사전순으로 가장 앞서는 답을 출력한다. 만약 덮을 수 없으면 -1을 출력한다.

내가 푼 풀이

- 주어진 보드판을 해당 폴리오미노로 바꾸면된다.

- 사전순으로 앞서는 답을 출력하기위해, A로 바꿀수있다면 최대한 A로 바꿔주면 된다.

 

 

import Foundation

var input = readLine()!
var arr = input.map{ String($0)}
var result = [String]()
var idx = 0

// 폴리오미노 덮기
// AAAA로 바꿀수 있다면 최대한 바꿔준다.
// 폴리오미노 둘다 길이가 2로 나눠지므로 만약 2로 나눠지지 않는다면 덮을 수 없다고 판단한다.
while idx < arr.count {
    if arr[idx] == "." {
        result.append(arr[idx])
        idx += 1
        continue
    }
    var count = 0
    while idx < arr.count && arr[idx] == "X" {
        count += 1
        idx += 1
    }
    while count > 0 {
        if count % 2 != 0 {
            result = ["-1"]
            break
        } else if count >= 4 {
            count -= 4
            result.append("AAAA")
        } else {
            count -= 2
            result.append("BB")
        }
    }
}
if result.contains("-1") {
    print("-1")
} else {
    print(result.joined(separator: ""))
}

문제

수열 A가 주어졌을 때, 가장 긴 감소하는 부분 수열을 구하는 프로그램을 작성하시오.

예를 들어, 수열 A = {10, 30, 10, 20, 20, 10} 인 경우에 가장 긴 감소하는 부분 수열은 A = {10, 30, 10, 20, 20, 10}  이고, 길이는 3이다.

입력

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000)이 주어진다.

둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000)

출력

첫째 줄에 수열 A의 가장 긴 감소하는 부분 수열의 길이를 출력한다.

내가 푼 풀이

- 이제까지 가장 긴 증가하는 부분수열에 대해 많이 풀어봤다.

- 같은 알고리즘에 등호만 다르다.

- dp[n] = a를 n번째원소까지의 감소하는 부분수열의 길이라 정의하자.

- 각 원소는 원소하나만으로 감소하는 부분수열의 길이 1을 갖는다.

- n번째 원소는 n-1번째 원소까지 값을 비교하며, n번째원소보다 작고, 중복되지 않는다면, dp[n]의 값을 증가시킨다.

 

예시로 주어진 [10,30,10,20,20,10] 수열에서

마지막원소 10을 검사할때, 이전의 원소들과 비교를 하게 된다.

20이 두 번 나와도, 첫 번째로 검사한 20에서 dp[n] 값을 증가시켰기 때문에

두 번째 20을 마주쳐도 dp[i] < dp[j] + 1 조건을 만족할 수 없기 때문에 중복되는 원소들을 배제시킬 수 있었다.

 

import Foundation

// 배열 입력받기
let count = Int(readLine()!)!
var arr = [0] + readLine()!.split(separator: " ").map{ Int($0)! }
var dp = Array(repeating: 1, count: count+1)
dp[0] = 0

// dp 입력
// i번째 원소보다 작은 j번째가 있는경우
// 해당하는 값이 이전에 나와있는지를 판단하기위해 dp[i] < dp[j] + 1 조건문을 걸었다.
for i in 1...count {
    for j in 1..<i {
        if arr[i] < arr[j] && dp[i] < dp[j] + 1 {
            dp[i] += 1
        }
    }
}
print(dp.max()!)

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

BOJ-1012 유기농 배추 Swift  (0) 2023.05.27
BOJ-1343 폴리오미노 Swift  (0) 2023.05.27
BOJ-2667 단지번호붙이기 Swift  (1) 2023.05.26
BOJ-1783 병든 나이트 Swift  (0) 2023.05.26
BOJ-2812 크게 만들기 Swift  (0) 2023.05.26

문제

<그림 1>과 같이 정사각형 모양의 지도가 있다. 1은 집이 있는 곳을, 0은 집이 없는 곳을 나타낸다. 철수는 이 지도를 가지고 연결된 집의 모임인 단지를 정의하고, 단지에 번호를 붙이려 한다. 여기서 연결되었다는 것은 어떤 집이 좌우, 혹은 아래위로 다른 집이 있는 경우를 말한다. 대각선상에 집이 있는 경우는 연결된 것이 아니다. <그림 2>는 <그림 1>을 단지별로 번호를 붙인 것이다. 지도를 입력하여 단지수를 출력하고, 각 단지에 속하는 집의 수를 오름차순으로 정렬하여 출력하는 프로그램을 작성하시오.

입력

첫 번째 줄에는 지도의 크기 N(정사각형이므로 가로와 세로의 크기는 같으며 5≤N≤25)이 입력되고, 그 다음 N줄에는 각각 N개의 자료(0혹은 1)가 입력된다.

출력

첫 번째 줄에는 총 단지수를 출력하시오. 그리고 각 단지내 집의 수를 오름차순으로 정렬하여 한 줄에 하나씩 출력하시오.

내가 푼 풀이

- 집이 있는곳을 기준으로 상하좌우에 중에 1이 있는 곳은 인접한 집이 있다는 것이고, 단지를 이룬다.

- 집이있지만 주변에 인접한 집이 없는 경우에도, 그 집 한 채는 단지를 이룬다고 본다.

- 주어진 N크기의 정사각형지도를 배열로 만들고, 인접한집의 리스트를 만든다.

- 여기서 주변에 인접한 집이 없어도, 자기집 하나로 단지를 이루기에 자신의 인덱스까지 리스트에 넣는다.

- graph[[Int]: [[Int]]] ----> [x, y]: [인접한 집의 x, y들]

- 위 문제에서는 집과 집사이의 순서는 필요 없기 때문에, BFS DFS 어떤 것이든 사용하든 상관없다.

- 그래프탐색을 이용해 인접한 집들을 모두 탐색하면, 단지의 집의 개수가 나온다.

- 나온 단지들의 개수와 단지 안에 있는 집의 개수를 오름차순으로 정렬 후 출력한다.

 

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

 

import Foundation

let size = Int(readLine()!)!
var arr = [[Int]]()
var graph = [[Int]: [[Int]]]()
var result = [Int]()

// 주어진 지도를 배열로 입력
for i in 0..<size {
    arr.append(readLine()!.map{Int(String($0))!})
}

// 배열을 통해 집과 인접한 집들의 위치를 저장한다.
// 주변에 인접한 집이 없어도, 자신의 집 한채로 단지를 이룬다고 보기에 자신의 위치 x,y도 리스트에 저장한다.
for i in 0..<size {
    for j in 0..<size {
        if arr[i][j] == 1 {
            if graph[[i,j]] == nil {
                graph[[i,j]] = [[i,j]]
            } else {
                graph[[i,j]]!.append([i,j])
            }
            if i > 0 && arr[i-1][j] == 1 {
                if graph[[i,j]] == nil {
                    graph[[i,j]] = [[i-1, j]]
                } else if !graph[[i,j]]!.contains([i-1,j]) {
                    graph[[i,j]]!.append([i-1,j])
                }
                if graph[[i-1,j]] == nil {
                    graph[[i-1,j]] = [[i, j]]
                } else if !graph[[i-1,j]]!.contains([i,j]) {
                    graph[[i-1,j]]!.append([i,j])
                }
            }
            if j < size-1 && arr[i][j+1] == 1 {
                if graph[[i,j]] == nil {
                    graph[[i,j]] = [[i, j+1]]
                } else if !graph[[i,j]]!.contains([i,j+1]) {
                    graph[[i,j]]!.append([i,j+1])
                }
                if graph[[i,j+1]] == nil {
                    graph[[i,j+1]] = [[i, j]]
                } else if !graph[[i,j+1]]!.contains([i,j]) {
                    graph[[i,j+1]]!.append([i,j])
                }
            }
            if i < size-1 && arr[i+1][j] == 1 {
                if graph[[i,j]] == nil {
                    graph[[i,j]] = [[i+1, j]]
                } else if !graph[[i,j]]!.contains([i+1,j]) {
                    graph[[i,j]]!.append([i+1,j])
                }
                if graph[[i+1,j]] == nil {
                    graph[[i+1,j]] = [[i, j]]
                } else if !graph[[i+1,j]]!.contains([i,j]) {
                    graph[[i+1,j]]!.append([i,j])
                }
            }
            if j > 0 && arr[i][j-1] == 1 {
                if graph[[i,j]] == nil {
                    graph[[i,j]] = [[i, j-1]]
                } else if !graph[[i,j]]!.contains([i,j-1]) {
                    graph[[i,j]]!.append([i,j-1])
                }
                if graph[[i,j-1]] == nil {
                    graph[[i,j-1]] = [[i, j]]
                } else if !graph[[i,j-1]]!.contains([i,j]) {
                    graph[[i,j-1]]!.append([i,j])
                }
            }
        }
    }
}


var totalVisited = [[Int]]()
var visitedQueue = [[Int]]()
var needVisitedQueue = [[Int]]()

// 그래프 탐색의 순서가 의미없기에, 배열을 순회하며 집을 발견하면 단지 탐색을 한다.
// BFS를 사용했다. (DFS를 사용해도 무관)
// 단지을 탐색후 발견한 집의 개수를 따로 저장하고, 발견한 집들의 위치는 다시 탐색할 필요가 없다.
for i in 0..<size {
    for j in 0..<size {
        if arr[i][j] == 1 && !totalVisited.contains([i,j]) {
            needVisitedQueue = [[i,j]]

            while !needVisitedQueue.isEmpty {
                var node = needVisitedQueue.removeFirst()
                if visitedQueue.contains(node) { continue }
                visitedQueue.append(node)

                needVisitedQueue += graph[node]!
            }
            totalVisited += visitedQueue
            result.append(visitedQueue.count)
            visitedQueue.removeAll()
        }
    }
}
result.sort{ $0 < $1 }
print(result.count)
for i in result {
    print(i)
}

문제

병든 나이트가 N × M 크기 체스판의 가장 왼쪽아래 칸에 위치해 있다. 병든 나이트는 건강한 보통 체스의 나이트와 다르게 4가지로만 움직일 수 있다.

  1. 2칸 위로, 1칸 오른쪽
  2. 1칸 위로, 2칸 오른쪽
  3. 1칸 아래로, 2칸 오른쪽
  4. 2칸 아래로, 1칸 오른쪽

병든 나이트는 여행을 시작하려고 하고, 여행을 하면서 방문한 칸의 수를 최대로 하려고 한다. 병든 나이트의 이동 횟수가 4번보다 적지 않다면, 이동 방법을 모두 한 번씩 사용해야 한다. 이동 횟수가 4번보다 적은 경우(방문한 칸이 5개 미만)에는 이동 방법에 대한 제약이 없다.

체스판의 크기가 주어졌을 때, 병든 나이트가 여행에서 방문할 수 있는 칸의 최대 개수를 구해보자.

입력

첫째 줄에 체스판의 세로 길이 N와 가로 길이 M이 주어진다. N과 M은 2,000,000,000보다 작거나 같은 자연수이다.

출력

병든 나이트가 여행에서 방문할 수 있는 칸의 개수중 최댓값을 출력한다.

내가 푼 풀이

- 체스판에서 나이트가 움직인다면, 위 아래는 자유자재로 움직이지만, 반드시 오른쪽으로 이동이 된다.

- 체스판에서 나이트의 움직임은 최대한 오른쪽으로 적게 가야 많이 움직이게 된다.

- 이는 나이트가 방문하는 칸의 최대가 되려면, 오른쪽으로 최대한 적게 움직여야 한다.

- 나이트의 최적의 움직임은 1번 4번을 반복해서 오른쪽으로 최대한 적게 움직이는 것이다.

- 나이트의 이동 횟수가 4번보다 적지 않다면 위 4가지 모두 한 번씩은 사용해야 한다.

- 이는 체스판의 크기 기준으로 4번 모두 움직일 수 있는 크기의 체스판이 주어진다면, 1번, 2번, 3번, 4번 움직임 모두 소화해 낸다.

- 반대로 크기가 작다면 최고효율의 움직임만 실행한다.

 

- 1번, 2번, 3번, 4번 모두 이동가능한 체스판의 크기는 어느 정도 일까

- 세로이동은 +2 , +1 , -1 , -2이다. 세로의 크기는 3칸 정도 되면 모두 움직일 수 있다.

- 가로이동은 +1 , +2 , +2 , +1이다. 가로의 크기는 7칸 정도 되면 모두 움직일 수 있다.

- 이는 나이트의 시작지점이 주어져있기 때문에, 3X7 크기 이상의 체스판이 주어진다면, 가능한 모든 움직임을 한 번씩 소화시킨다.

  O       O  
      O      
시작점           O

위와 같이 모든 움직임을 한번씩 소화했을 때의 경우이다.

 

- 세로의 크기가 3보다 작다면

세로의 크기가 1이면 움직일 수 없다.

세로의 크기가 2라면

    O    
시작점       O

위와 같이 2번, 3번 이동만 가능하지만, 이동방법을 모두 사용하지 않았으므로 방문가능한 지점은 가로가 아무리 길어도 최대 4가 된다.

 

 

- 가로의 크기가 7보다 작다면

  O   O  
         
시작점   O   O

위와 같이 1번, 4번 이동으로 최대한 많은 지점을 방문했지만, 이동방법을 모두 사용하지 않았으므로 방문가능한 지점은 최대 4가 된다.

 

 

위의 경우바탕으로 코드로 구현해 보자.

import Foundation

var input = readLine()!.split(separator: " ").map{ Int($0)! }
var result = 1
var num = 0
var idx = 1

// 세로의 길이를 기준으로 길이가 1이면 이동 불가능
// 길이가 2이면 2,3번 이동방법 가능
if input[0] == 2 {
    result = (input[1] + 1) / 2
    if result > 4 {
        result = 4
    }
} else if input[0] > 2 {
    // 가로의길이가 7보다 크다면, 모든 이동방법 이용가능
    // 모든 이동방법을 한번씩 사용한 후, 남은 크기를 가장 효율적인 이동인 1번,4번으로만 이동
    if input[1] > 6 {
        result = 5
        idx = 7
        num = input[1] - 7
        result = result + num
    } else {
        // 가로의 길이가 7보다 작아도, 가장 효율적인 이동인 1번,4번을 이용하지만
        // 모든 이동방법을 사용하지 않았으므로 방문지점은 최대4가된다.
        result = result + input[1] - 1
        if result > 4 {
            result = 4
        }
    }
}

print(result)

문제

N자리 숫자가 주어졌을 때, 여기서 숫자 K개를 지워서 얻을 수 있는 가장 큰 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N과 K가 주어진다. (1 ≤ K < N ≤ 500,000)

둘째 줄에 N자리 숫자가 주어진다. 이 수는 0으로 시작하지 않는다.

출력

입력으로 주어진 숫자에서 K개를 지웠을 때 얻을 수 있는 가장 큰 수를 출력한다.

내가 푼 풀이

- N자리 숫자에서 K개를 지울 때, 가장 큰 수로 만들려면, 앞자리일수록 수가 커야 한다.

- 이 말은 N자리 숫자를 지워서 재배열하는 것이 아닌, 단순히 그 위치에 있는 숫자를 지우기 때문에, 앞자리 쪽일수록 큰 수가 배치되어야 큰 수가 되는 것이다.

- 앞자리 인덱스부터 스택에 담아서, 그다음 인덱스의 숫자가 클 경우, 스택에 들어가 있는 숫자들 중 작은 값들을 모두 지워낸다.

- 예로 스택에 [1,2,3] 이 담겨있을 때, 그다음 숫자가 4라면 스택에 있는 4보다 작은 숫자를 K횟수만큼 빼낸다.

- 위 방법은 만약 숫자가 내림차순이 된 경우, 다음 인덱스와 비교해도, 큰 수가 나오지 않기 때문에

54321 인경우 앞자리부터 이미 큰 값으로 배치가 되어있으므로 K횟수만큼 pop 한다.

 

예제)

7자리 숫자 1231234에서 숫자 3개를 지웠을 때

 

숫자 1 :

  • stack: []
  • 스택이 비어있으므로 넣는다.

숫자 2 :

  • stack: [1]
  • 스택에서 2보다 작은 숫자들은 pop 하고, 2를 넣는다
  • stack: [2]

숫자 3 :

  • stack: [2]
  • 스택에서 3보다 작은숫자들은 pop하고, 3을 넣는다.
  • stack: [3]

숫자 1 :

  • stack: [3]
  • 스택에서 1보다 작은숫자들은 없다. 1을 넣는다.
  • stack: [3, 1]

숫자 2 :

  • stack: [3, 1]
  • 스택에서 2보다 작은 숫자 1을 pop하고, 2을 넣는다.
  • stack: [3, 2]
  • 여기서 3번의 횟수를 모두 사용하였다.

숫자 3 :

  • stack: [3, 2]
  • 스택에서 3보다 작은숫자 2가 있지만, 이미 제거 횟수를 모두 썼기에, 3을 넣는다.
  • stack: [3, 2, 3]

숫자 4 :

  • stack: [3, 2, 3]
  • 스택에서 4보다 작은 숫자들이 있지만, 이미 제거 횟수를 모두 썼기에, 4를 넣는다.
  • stack: [3, 2, 3, 4]

이로써 만들어 낼 수 있는 가장 큰 수는 3234가 된다.

 

위 방식대로 코드로 구현해 보자.

 

import Foundation

let input = readLine()!.split(separator: " ").map{ Int($0)! }
var rmCount = input[1]
var num = readLine()!.map{ Int(String($0))! }
var stack = [Int]()

// 앞자리부터 스택에 넣는다.
// 스택의 맨 뒤가 현재 원소보다 작을 경우, 현재 원소보다 작은값들은 k번 만큼 지워준다.
// 숫자가 내림차순으로 되있는 경우, k번 지울 수 없기에 뒷부분을 남은 횟수만큼 지워준다.
for i in 0..<input[0] {
    if stack.isEmpty {
        stack.append(num[i])
        continue
    }
    while !stack.isEmpty && stack[stack.count - 1] < num[i] && rmCount > 0 {
        rmCount -= 1
        stack.popLast()
    }
    stack.append(num[i])
}

if rmCount > 0 {
    while rmCount > 0 {
        rmCount -= 1
        stack.popLast()
    }
}
print(stack.map{ String($0)}.joined(separator: ""))

문제

어떤 자연수 N은 그보다 작거나 같은 제곱수들의 합으로 나타낼 수 있다. 예를 들어 $$11= 3^{2}+1^{2}+1^{2}$$ (3개 항)이다. 이런 표현방법은 여러 가지가 될 수 있는데, 11의 경우 $$11= 2^{2}+2^{2}+1^{2}+1^{2}+1^{2}$$(5개 항)도 가능하다. 이 경우, 수학자 숌크라테스는 “11은 3개 항의 제곱수 합으로 표현할 수 있다.”라고 말한다. 또한 11은 그보다 적은 항의 제곱수 합으로 표현할 수 없으므로, 11을 그 합으로써 표현할 수 있는 제곱수 항의 최소 개수는 3이다.

주어진 자연수 N을 이렇게 제곱수들의 합으로 표현할 때에 그 항의 최소개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 자연수 N이 주어진다. (1 ≤ N ≤ 100,000)

출력

주어진 자연수를 제곱수의 합으로 나타낼 때에 그 제곱수 항의 최소 개수를 출력한다.

내가 푼 풀이

- 처음에는 단순히 자연수 N과 크기가 비슷하고 작은 제곱수부터 빼면서 카운트를 했는데 실패가 떴다.

$$43 = 6^{2}+7 = 6^{2}+2^{2}+1^{2}+1^{2}+1^{2}$$

$$43 = 5^{2}+18 = 5^{2}+3^{2}+3^{2}$$

- 43은 위와 같이 N보다 작은 값중에 가장 큰 값을 빼가며 카운트하는 것은 정답이 아니게 될 수 있다.

- 가장 큰값을 빼가는 그리디방식은 이문제와 맞지 않다.

- dp[n] = 자연수 n의 제곱수의 항의 최소 개수라 정의하자.

- 제곱수 항의 개수가 최대가 되는 방법은 모든 제곱수가 1의 제곱이 되는 것이다.

- 초기값은 dp[n] = arr[n]이다.

- 1부터 n의 제곱근까지 자연수 n을 빼가며, 항의 최소개수를 입력한다.

- dp[n] = min(dp[n], dp[n - idx * idx] + 1) ( idx는 1부터 n의 제곱근까지의 범위)

 

이를 코드로 구현해 보자

 

import Foundation

var num = Int(readLine()!)!
var dp = [0] + Array(1...num)

// dp 입력
// dp[n]의 제곱수항의 갯수 최댓값은 모든 제곱수가 1인것
// n이 제곱수라면 dp[0] + 1 인 값이 최솟값이 되는것이다.
for i in 1...num {
    var idx = 1
    while idx * idx <= i {
        dp[i] = min(dp[i], dp[i - idx * idx] + 1)
        idx += 1
    }
}
print(dp[num])

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

BOJ-1783 병든 나이트 Swift  (0) 2023.05.26
BOJ-2812 크게 만들기 Swift  (0) 2023.05.26
BOJ-11055 가장 큰 증가하는 부분 수열 Swift  (0) 2023.05.26
BOJ-2606 바이러스 Swift  (0) 2023.05.25
BOJ-3109 빵집 Swift  (1) 2023.05.25

+ Recent posts