안녕하세요.
오늘은 백준 7579번: 앱(링크) 문제를 풀어보려고 합니다.
문제
우리는 스마트폰을 사용하면서 여러 가지 앱(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 바이트를 확보하기 위한 앱 비활성화의 최소의 비용을 계산하여 한 줄에 출력해야 한다.
예제 입력
5 60
30 10 20 35 40
3 0 3 5 4
예제 출력
6
제한
시간 제한: 1초
메모리 제한: 128MB
풀이
배낭 문제에서 조금 응용한 문제이다.
먼저 배낭 문제에서 사용한 점화식을 보면 아래와 같다.
i번째의 물건을 넣지 못하는 경우는 dp[i-1][w]를 그대로 가져온다.
i번째 물건을 넣을 수 있을 때 1) 넣는 경우 2) 안 넣는 경우 중에서 큰 값을 취한다.
1) 넣는 경우: i번째 물건의 가치 v_i + i-1번째까지 탐색했을 때 무게가 w - w_i인 최대 이익
i번째 물건을 넣었을 경우, 무게가 w가 되려면 i-1번째까지 탐색했을 때 무게가 w - w_i인 최대 이익을 사용해야 한다.
2) 안 넣는 경우: i-1번째까지 탐색했을 때 무게가 w인 최대 이익
위의 공식을 비슷하게 사용해 이번 문제에 적용하면 아래처럼 될 수 있다.
하지만 메모리의 최대 크기가 천만이라서 메모리 초과로 배열로 만들 수가 없다.
따라서 생각을 조금 틀어서, 메모리가 아닌 비용을 배열의 인자값으로 넣어볼 수가 있다.
이때 dp 배열을 정의하면 아래와 같다.
여기서 최소 메모리가 아닌 최대 메모리 크기로 한 이유는, 같은 비용이어도 최대 메모리로 잡아야지 나중에 계산해가면서 최종적으로 나타나는 비용 c가 적어지기 때문이다.
최종적으로 위의 배낭 공식을 비슷하게 적용하면 아래와 같은 식이 만들어진다.
위 공식을 따라 c = 0 ~ 최대 비용 크기까지 계산을 이어나가면 된다.
계산을 다 마친 후에 dp 배열의 값(메모리 크기)가 원하는 M보다 크거나 같을 때를 찾고 그 때의 비용 c를 출력하면 된다.
여기서 주의해야 할 점은, dp[i][c]에서 i=1에 대해 c=0~최대비용을 찾는 것이 아니라 c=0에 대해 i=1~n까지로 찾아야 한다는 점이다.
// 잘못된 방식
for (int i = 1; i <= n; i++) {
for (int c = 0; c <= totalCost; c++) {
if (dp[i][c] >= m) {
cout << c;
return 0;
}
}
}
// 옳은 방식
// 메모리 접근을 효율적으로 하기 위해 배열을 선언할 때 dp[c][i] 형태로 해주면 더 좋다.
for (int c = 0; c <= totalCost; c++) {
for (int i = 1; i <= n; i++) {
if (dp[i][c] >= m) {
cout << c;
return 0;
}
}
}
즉, 작은 비용부터 검사해야지 올바른 답이 나온다.
정답
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
using ll = long long;
using pr = pair<ll, ll>;
int n, m;
ll mem[101];
ll cost[101];
ll dp[101][10001] = { 0 };
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin >> n >> m;
ll curMem = 0;
for (int i = 1; i <= n; i++) {
cin >> mem[i];
}
int totalCost = 0;
for (int i = 1; i <= n; i++) {
cin >> cost[i];
totalCost += cost[i];
}
for (int i = 1; i <= n; i++) {
for (int c = 0; c <= totalCost; c++) {
if (cost[i] > c) {
dp[i][c] = dp[i - 1][c];
}
else {
dp[i][c] = max(mem[i] + dp[i - 1][c - cost[i]], dp[i - 1][c]);
}
}
}
for (int c = 0; c <= totalCost; c++) {
for (int i = 1; i <= n; i++) {
if (dp[i][c] >= m) {
cout << c;
return 0;
}
}
}
return 0;
}
이렇게 해서 백준 7579번 앱 문제를 풀어보았습니다.
댓글로 질문을 남기시면 성실히 답해드리겠습니다
감사합니다!
'PS > 백준 문제' 카테고리의 다른 글
[BOJ][C++] 11444번 피보나치 수 6 (0) | 2024.10.14 |
---|---|
[BOJ][C++] 10830번 행렬 제곱 (0) | 2024.10.14 |
[BOJ][C++] 25682번 체스판 다시 칠하기 2 (3) | 2024.10.10 |
[BOJ][C++] 11049번 행렬 곱셈 순서 (0) | 2024.10.10 |
[BOJ][C++] 2437번 저울 (0) | 2024.05.04 |