문제
우리는 스마트폰을 사용하면서 여러 가지 앱(App)을 실행하게 된다. 대개의 경우 화면에 보이는 ‘실행 중’인 앱은 하나뿐이지만 보이지 않는 상태로 많은 앱이 '활성화'되어 있다. 앱들이 활성화 되어 있다는 것은 화면에 보이지 않더라도 메인 메모리에 직전의 상태가 기록되어 있는 것을 말한다. 현재 실행 중이 아니더라도 이렇게 메모리에 남겨두는 이유는 사용자가 이전에 실행하던 앱을 다시 불러올 때에 직전의 상태를 메인 메모리로부터 읽어 들여 실행 준비를 빠르게 마치기 위해서이다.
하지만 스마트폰의 메모리는 제한적이기 때문에 한번이라도 실행했던 모든 앱을 활성화된 채로 메인 메모리에 남겨두다 보면 메모리 부족 상태가 오기 쉽다. 새로운 앱을 실행시키기 위해 필요한 메모리가 부족해지면 스마트폰의 운영체제는 활성화 되어 있는 앱들 중 몇 개를 선택하여 메모리로부터 삭제하는 수밖에 없다. 이러한 과정을 앱의 ‘비활성화’라고 한다.
메모리 부족 상황에서 활성화 되어 있는 앱들을 무작위로 필요한 메모리만큼 비활성화 하는 것은 좋은 방법이 아니다. 비활성화된 앱들을 재실행할 경우 그만큼 시간이 더 필요하기 때문이다. 여러분은 이러한 앱의 비활성화 문제를 스마트하게 해결하기 위한 프로그램을 작성해야 한다
현재 N개의 앱, A1, ..., AN이 활성화 되어 있다고 가정하자. 이들 앱 Ai는 각각 mi 바이트만큼의 메모리를 사용하고 있다. 또한, 앱 Ai를 비활성화한 후에 다시 실행하고자 할 경우, 추가적으로 들어가는 비용(시간 등)을 수치화 한 것을 ci 라고 하자. 이러한 상황에서 사용자가 새로운 앱 B를 실행하고자 하여, 추가로 M 바이트의 메모리가 필요하다고 하자. 즉, 현재 활성화 되어 있는 앱 A1, ..., AN 중에서 몇 개를 비활성화 하여 M 바이트 이상의 메모리를 추가로 확보해야 하는 것이다. 여러분은 그 중에서 비활성화 했을 경우의 비용 ci의 합을 최소화하여 필요한 메모리 M 바이트를 확보하는 방법을 찾아야 한다.
입력
입력은 3줄로 이루어져 있다. 첫 줄에는 정수 N과 M이 공백문자로 구분되어 주어지며, 둘째 줄과 셋째 줄에는 각각 N개의 정수가 공백문자로 구분되어 주어진다. 둘째 줄의 N개의 정수는 현재 활성화 되어 있는 앱 A1, ..., AN이 사용 중인 메모리의 바이트 수인 m1, ..., mN을 의미하며, 셋째 줄의 정수는 각 앱을 비활성화 했을 경우의 비용 c1, ..., cN을 의미한다
단, 1 ≤ N ≤ 100, 1 ≤ M ≤ 10,000,000이며, 1 ≤ m1, ..., mN ≤ 10,000,000을 만족한다. 또한, 0 ≤ c1, ..., cN ≤ 100이고, M ≤ m1 + m2 + ... + mN이다.
출력
필요한 메모리 M 바이트를 확보하기 위한 앱 비활성화의 최소의 비용을 계산하여 한 줄에 출력해야 한다.
내가 푼 풀이
접근방법: DP(배낭문제)
주어진 앱중 가장 효율적인(비용이 적은)앱을 비활성화 하여 메모리 할당을 하는것이다.
이것은 무게가 서로다른 보석을 가장 비싸게 담아내는 배낭문제와 유사했다.
평소의 배낭문제였다면, 무게와 가치순으로 dp테이블을 구성하여 최댓값을 구해냈지만 위 문제에서 메모리는 최대 1000만이다.
메모리로 구한다면 시간초과가 날것이므로 생각을 다르게 해서 dp테이블을 구성해보고자 한다.
n번째 앱까지 비활성화했을때의 최소비용 --> n번째 앱까지 비활성화했을때, 할당되는 메모리
n번째 앱까지 비활성화했을때의 최소비용을 구한다면, 메모리를 1부터 최대 1000만까지 순회해야하므로 시간초과
n번째 앱까지 비활성화했을때의 할당되는 메모리 를 구한다면, N이 최대 100이고, 비용도 100이므로 10000 만큼 순회한다.
예시문제에서 첫번째 앱을 비활성화 한다면 다음과 같이 나타낼 수 있다.
비용 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | ... |
1 | 0 | 0 | 0 | 30 | 30 | 30 | 30 | ... |
첫번째 앱의 메모리는 30, 비용은 3이다.
이는 비용 3미만일 때, 비활성화할 수 없으므로 0을 넣고, 3부터는 비활성화를 할 수 있다.
비용 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | ... |
1 | 0 | 0 | 0 | 30 | 30 | 30 | 30 | ... |
2 | 10 | 10 | 10 | 40 | 40 | 40 | 40 | ... |
두번째 앱의 메모리는 10, 비용은 0이다.
이는 비용이 0일때, 비활성화가 가능하므로 메모리를 할당받을 수 있다.
이때, 비용이 3이 주어졌을 때 첫번째 앱과 두번째 앱 모두 비활성화가 가능하므로 이전 행에 있던 값을 불러와 더해준다.
비용 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | ... |
1 | 0 | 0 | 0 | 30 | 30 | 30 | 30 | ... |
2 | 10 | 10 | 10 | 40 | 40 | 40 | 40 | ... |
3 | 10 | 10 | 10 | 40 | 40 | 40 | 60 | ... |
세번째 앱의 메모리는 20, 비용은 3이다.
비용이 0일때 두번째 앱을 비활성화 할 수 있으므로 이전 행에 있는 값을 불러온다.
비용이 3일때, 경우의수는 두가지다.
1. 첫번째(3), 두번째(0)을 비활성화 하는경우
2. 두번째(0), 세번째(3)을 비활성화 하는경우
1번 경우의수는 이전행에 저장되있는 값이다(40)
2번 경우의수는 이전행의 두번째비용를 뺀 위치에 있는 값과 세번째 앱의 메모리를 더한 값이 된다.(10+20)
이를 식으로 나타내면
1번: dp[i-1][j]
2번: dp[i-1][j-a] + m ( a: 비용, m: 할당되는메모리)
점화식으로 나타내면 dp[i][j] = max(dp[i-1][j], dp[i-1][j-a] + m) 이 된다.
비용이 6이 주어졌을 때,
dp[3][6] = max( dp[2][6] , dp[2][3] + 20(세번째앱의 메모리)) = 60이 되는것이다.
이를 코드로 구현하면 다음과 같다.
import Foundation
// 입력받기
let input = readLine()!.split(separator: " ").map{Int(String($0))!}
let N = input[0], M = input[1]
var arr = [[0]]
let a1 = readLine()!.split(separator: " ").map{Int(String($0))!}
let a2 = readLine()!.split(separator: " ").map{Int(String($0))!}
var sum = a2.reduce(0, +)
var answer = Int.max
for i in 0..<N {
arr.append([a1[i],a2[i]])
}
// dp[i][j]: i번째 앱, j의 비용을 들였을때, 할당되는 메모리
var dp = Array(repeating: Array(repeating: 0, count: sum + 1), count: N+1)
// 배낭문제
// dp[i][j]: max(dp[i-1][j], dp[i-1][j-a] + m) (a:비용, m:메모리)
for i in 1...N {
for j in 0...sum {
dp[i][j] = dp[i-1][j]
if j - arr[i][1] >= 0 {
dp[i][j] = max(dp[i][j], dp[i-1][j-arr[i][1]] + arr[i][0])
}
// 메모리 할당이 충분히 됬다면, 비용을 저장하고 최솟값 갱신
if dp[i][j] >= M {
answer = min(answer, j)
}
}
}
print(answer)
'코딩테스트 > 백준' 카테고리의 다른 글
BOJ-9370 미확인 도착지 Swift (1) | 2024.06.03 |
---|---|
BOJ-1707 이분 그래프 Swift (0) | 2024.06.03 |
BOJ-2011 암호코드 Swift (0) | 2024.05.19 |
BOJ-2302 극장 좌석 Swift (0) | 2024.05.16 |
BOJ-1937 욕심쟁이 판다 Swift (0) | 2024.05.14 |