문제 유형 중, DP의 추천문제들을 풀고 있습니다.
DP2의 문제양이 많아, DP2-1과 DP2-2로 나눕니다. (DP2-1은 여기서 확인할 수 있습니다.)
추천문제 번호 모음은 여기서 확인할 수 있습니다. 골드 문제입니다.
문제 풀이를 할 때 마다 계속 추가됩니다.
cmd + F 를 통해 문제번호 찾기를 추천드립니다.
22.06.03 업데이트
22.06.06 업데이트
22.06.07 업데이트
https://github.com/SuminPark-developer/BaekJoonPS
https://developer-p.tistory.com/201
https://developer-p.tistory.com/203
백준 Swift 1520번
문제 유형 : DFS + DP
처음에 DFS만으로 푸는 건 풀었다. 혹시나 했지만 역시나 20~30%즈음에서 시간초과...
못풀겠어서 다른 블로그를 찾아보니, DFS와 DP를 합쳐서 푸는 문제였다.
보통 DFS를 풀 땐 방문체크를 한다. DP에서 방문체크를 하기 위해선 어떻게 해야 할까?
dp를 -1로 초기화 한 뒤, -1값이라는건 첫방문이라는 뜻이다.
첫방문일 시, dp값을 0으로 바꿔주고, dfs를 다시 시작한다.
첫방문이 아닐 시, 저장된 값을 바로 return해준다. 저장된 값은 몇개의 길이 있는지에 대한 정보를 담고 있다.
목적지에 도달했을 때 1값을 return해준다.
하단 그림을 참고하자.
목적지에 도달할 때 1을 리턴해줘서, 탐색하는건 (그림기준) 위에서부터인데, 저장되는건 밑에서부터이다.
난이도 : 골드4
참고 자료 : https://sihyungyou.github.io/baekjoon-1520/ | https://wootool.tistory.com/83
DFS + DP
// MARK: - 1520번(DFS + DP)
let input = readLine()!.split(separator: " ").map{Int(String($0))!}
let (M, N) = (input[0], input[1])
var arr: [[Int]] = Array(repeating: [], count: M + 1)
arr[0] = Array(repeating: 0, count: N + 1)
for i in 1...M {
arr[i] = [0] + readLine()!.split(separator: " ").map{Int(String($0))!}
}
var dp: [[Int]] = Array(repeating: Array(repeating: -1, count: N + 1), count: M + 1)
func isValidCoord(_ y: Int, _ x: Int) -> Bool { // 유효범위 체크.
return (1 <= y && y <= M) && (1 <= x && x <= N)
}
func isEscape(_ y: Int, _ x: Int) -> Bool { // 도착완료 체크.
return y == M && x == N
}
let dy = [0, 1, 0, -1]
let dx = [1, 0, -1, 0]
func dfs(_ y: Int, _ x: Int) -> Int {
// print(y, x, dp)
if isEscape(y, x) { // 도착 시,
return 1
}
// if dp[y][x] != -1 { // 방문 좌표일 시,
// return dp[y][x]
// }
if dp[y][x] == -1 { // 미방문 좌표일 시,
dp[y][x] = 0 // 우선 값 0으로 초기화.
for k in 0..<4 {
let ny = y + dy[k]
let nx = x + dx[k]
if isValidCoord(ny, nx) && arr[y][x] > arr[ny][nx] { // 유효범위이고, 높이가 낮으면,
dp[y][x] += dfs(ny, nx)
}
}
}
return dp[y][x] // 방문 좌표일 시,
}
let answer = dfs(1, 1)
print(answer)
Only DFS - 시간초과
// MARK: - 1520번(DFS)
let input = readLine()!.split(separator: " ").map{Int(String($0))!}
let (M, N) = (input[0], input[1])
var arr: [[Int]] = Array(repeating: [], count: M + 1)
arr[0] = Array(repeating: 0, count: N + 1)
for i in 1...M {
arr[i] = [0] + readLine()!.split(separator: " ").map{Int(String($0))!}
}
//var dp: [[Int]] = Array(repeating: Array(repeating: -1, count: N + 1), count: M + 1)
func isValidCoord(_ y: Int, _ x: Int) -> Bool { // 유효범위 체크.
return (1 <= y && y <= M) && (1 <= x && x <= N)
}
func isEscape(_ y: Int, _ x: Int) -> Bool { // 도착완료 체크.
return y == M && x == N
}
let dy = [0, 1, 0, -1]
let dx = [1, 0, -1, 0]
var answer: Int = 0
func dfs(_ y: Int, _ x: Int) {
if isEscape(y, x) {
answer += 1
return
}
for k in 0..<4 {
let ny = y + dy[k]
let nx = x + dx[k]
if isValidCoord(ny, nx) && arr[y][x] > arr[ny][nx] {
dfs(ny, nx)
}
}
}
dfs(1, 1)
print(answer)
백준 Swift 2056번
문제 유형 : DP(반복문) / 위상 정렬(Topology Sort)
전에 위상정렬 관련 문제를 풀었었다. 근데 풀이 방법을 잊고 있었다. 위상정렬을 기억하고 있었다면, 이 문제를 보고 바로 알아챌 수 있었을 것 같은데 아쉽다. 결국 다른 블로그를 참고했다.
이 문제는 DP만으로 풀 수 있었고, 위상 정렬을 이용해서도 풀 수 있었다.
DP 풀이
미리 받아놓고 나중에 for문을 돌리는 식이 아니다.
1. i번부터 N번까지의 작업을 돌면서,
2. j를 count 개수만큼 선행 관계를 돌면서,
let beforeWorkNum = input[2 + j] // 선행 관계 작업 번호
// dp[i] = max(dp[i], dp[beforeWorkNum] + time) (ver.1)
dp[i] = max(dp[i], dp[beforeWorkNum]) // (ver.2)
3. 구한 dp값에, i번 작업에 걸리는 시간을 더해주면 된다.
dp[i] += time // i번 작업에 걸리는 시간을 추후에 저장. (ver.2)
4. dp배열의 최대값이 정답이 된다.
위상정렬 풀이
1. 연결된 그래프를 저장한다. 각 작업의 진입차수도 저장한다.
2. 진입차수가 0인 것만 큐에 넣는다.
3. 큐가 empty가 될 때까지 돌면서,
3-1. q.popFirst()를 한다. 우선, 해당 작업(node)에 걸리는 시간을 저장한다.
let node = q.popFirst()
completeTimeArray[node] += times[node] // 우선, 해당 작업(현재꺼)에 걸리는 시간 저장.
3-2. 현재 노드(node)와 연결된 노드(nextNode)들을 돌면서,
연결된 노드(nextNode)의 진입차수를 1 감소 시키고,
연결된 노드와 현재 노드의 값중 최대값을 저장한다.
연결된 노드의 진입 차수가 0이라면 큐에 집어 넣는다.
for nextNode in graph[node] { // 현재 노드와 연결된 노드들을 돌면서,
indegree[nextNode] -= 1 // 연결된 노드의 진입 차수를 -1씩 감소.
completeTimeArray[nextNode] = max(completeTimeArray[nextNode], completeTimeArray[node]) // 다음꺼 걸리는 시간, 현재꺼 걸리는 시간
if indegree[nextNode] == 0 { // 진입 차수가 0이라면,
q.pushLast(nextNode) // 큐에 저장.
}
}
난이도 : 골드5
참고 자료 : https://m.blog.naver.com/ndb796/221236874984 | https://steady-coding.tistory.com/182
큐 - 위상정렬
// MARK: - 2056번(큐 - 위상정렬)
class Dequeue<T> {
var enQueue: [T]
var deQueue: [T] = []
var count: Int {
return enQueue.count + deQueue.count
}
var isEmpty: Bool {
return enQueue.isEmpty && deQueue.isEmpty
}
init(_ queue: [T]) {
enQueue = queue
}
func pushLast(_ element: T) {
enQueue.append(element)
}
func pushFirst(_ element: T) {
deQueue.append(element)
}
func popLast() -> T {
if enQueue.isEmpty {
enQueue = deQueue.reversed()
deQueue.removeAll()
}
return enQueue.popLast()!
}
func popFirst() -> T {
if deQueue.isEmpty {
deQueue = enQueue.reversed()
enQueue.removeAll()
}
return deQueue.popLast()!
}
}
let N = Int(readLine()!)!
var graph: [[Int]] = Array(repeating: [], count: N + 1)
var times: [Int] = [-1] + Array(repeating: 0, count: N) // 해당 작업에 걸리는 시간들 저장.
var indegree: [Int] = [-1] + Array(repeating: 0, count: N) // 각 작업들의 진입 차수 저장.
for i in 1...N {
let input = readLine()!.split(separator: " ").map{Int(String($0))!}
times[i] = input[0]
let count: Int = input[1]
indegree[i] = count // 진입 차수 저장.
for j in 0..<count {
let beforeWorkNum = input[2 + j] // 선행 관계 작업 번호
graph[beforeWorkNum].append(i)
}
}
let q: Dequeue<Int> = Dequeue([])
for i in 1...N {
if indegree[i] == 0 {
q.pushLast(i)
}
}
var completeTimeArray: [Int] = [-1] + Array(repeating: 0, count: N) // 각 작업을 완료하는데 걸리는 시간.
while !q.isEmpty {
let node = q.popFirst()
completeTimeArray[node] += times[node] // 우선, 해당 작업(현재꺼)에 걸리는 시간 저장.
for nextNode in graph[node] { // 현재 노드와 연결된 노드들을 돌면서,
indegree[nextNode] -= 1 // 연결된 노드의 진입 차수를 -1씩 감소.
completeTimeArray[nextNode] = max(completeTimeArray[nextNode], completeTimeArray[node]) // 다음꺼 걸리는 시간, 현재꺼 걸리는 시간
if indegree[nextNode] == 0 { // 진입 차수가 0이라면,
q.pushLast(nextNode) // 큐에 저장.
}
}
}
print(completeTimeArray.max()!)
반복문 - DP
// MARK: - 2056번(DP - 반복문)
let N = Int(readLine()!)!
var dp: [Int] = [-1] + Array(repeating: 0, count: N)
for i in 1...N { // 작업을 돌면서,
let input = readLine()!.split(separator: " ").map{Int(String($0))!}
let time = input[0] // i번 작업에 걸리는 시간.
let count: Int = input[1] // 선행 관계에 있는 작업 개수
// dp[i] = time // i번 작업에 걸리는 시간을 우선 저장. (ver.1)
for j in 0..<count { // 선행 관계를 돌면서,
let beforeWorkNum = input[2 + j] // 선행 관계 작업 번호
// dp[i] = max(dp[i], dp[beforeWorkNum] + time) (ver.1)
dp[i] = max(dp[i], dp[beforeWorkNum]) // (ver.2)
}
dp[i] += time // i번 작업에 걸리는 시간을 추후에 저장. (ver.2)
}
print(dp.max()!)
백준 Swift 1695번
문제 유형 : DP(재귀, 반복문) / LCS(최장 공통 부분 수열)
고민하다보니 LIS가 생각났다. 근데 다시 생각해보니 increase는 전혀 상관이 없었다. 그러다 결국 찾지 못하고 다른 블로그를 참고했다.
풀이
arr과 reverseArr의 LCS를 구하면 된다.
숫자가 같다면, dp[i][j] = dp[i - 1][j - 1] + 1
숫자가 다르다면, dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) // 이전 행의 같은 열, 같은 행의 이전 열 중 큰 값을 저장.
그 다음, 총 길이 N에서 LCS값인 dp[N][N]을 빼면 된다.
난이도 : 골드4
참고 자료 : https://ddiyeon.tistory.com/67 | https://t.ly/iQq7 | https://mapocodingpark.blogspot.com/2020/07/1695.html
재귀
// MARK: - 1695번(DP - 재귀함수)
let N = Int(readLine()!)!
var arr: [Int] = [-1] + readLine()!.split(separator: " ").map{Int(String($0))!}
var dp: [[Int]] = Array(repeating: Array(repeating: -1, count: N + 1), count: N + 1)
func topDown(_ start: Int, _ end: Int) -> Int {
if start >= end {
return 0
}
if dp[start][end] != -1 {
return dp[start][end]
}
if arr[start] == arr[end] { // 시작과 끝 값이 같을 때,
dp[start][end] = topDown(start + 1, end - 1)
}
else { // 다를다면,
dp[start][end] = min(topDown(start + 1, end), topDown(start, end - 1)) + 1 // min(우측에 좌측값을 넣을 때, 좌측에 우측값을 넣을 때) + 1
}
return dp[start][end]
}
let answer = topDown(1, N)
print(answer)
반복문
// MARK: - 1695번(DP - 반복문)
let N = Int(readLine()!)!
var arr: [Int] = [-1] + readLine()!.split(separator: " ").map{Int(String($0))!}
//var rArr = arr.reversed() // arr을 뒤집음.
var dp: [[Int]] = Array(repeating: Array(repeating: 0, count: N + 1), count: N + 1)
for i in 1...N { // 뒤부터 돈 것(rArr)과,
for j in 1...N { // 앞부터 돈 것(arr)에서,
if arr[N + 1 - i] == arr[j] { // 숫자가 같다면,
dp[i][j] = dp[i - 1][j - 1] + 1
}
else { // 숫자가 다르다면,
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) // 이전 행의 같은 열, 같은 행의 이전 열 중 큰 값을 저장.
}
}
}
print(N - dp[N][N])
백준 Swift 21923번
문제 유형 : DP(재귀, 반복문)
- 느낀점, 설명설명~~~
난이도 : 골드4
반복문
import Foundation
최근댓글