본문 바로가기

알고리즘 Algorithm/BOJ 백준 (초급~중급)

[BOJ 백준] 앱 (7579) Java

링크 : https://www.acmicpc.net/problem/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

 

예제 출력 : 

 

 

접근법 : 

1) 어떻게 풀 것인가?

앱의 탈을 쓴 배낭(Knapsack) 알고리즘 문제이다.

 

dp[ i ][ j ] 를 i번째 앱까지 확인했을때, j cost를 소모해서 얻을 수 있는 최대 메모리

라고 정의할 수 있다.

 

① 냅색 알고리즘을 실행하고

		// 2. 냅색 알고리즘 실행
		for (int i = 1; i <= N; i++)
		{
			for (int j = 0; j <= costSum; j++)
			{
				// 2-1. 갱신이 가능하면 둘 중 큰 값 넣어주기
				if (j - array[i].cost >= 0) {
					dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - array[i].cost ] + array[i].mem );
				}
				// 2-2. 아니면 원래 있던 값
				dp[i][j] = Math.max(dp[i][j], dp[i - 1][j]);
			}
		}

 

② 최소비용 확인시에는 dp[N][ j ]에서 j 를 최솟값부터 j++하면서 

   M보다 크거나 같아지면 종료하면 된다.

// 3. 최소비용 확인
		for (int i = 0; i <= costSum; i++)
		{
			// M바이트 확보한 최소비용
			if (dp[N][i] >= M)
			{
				ans = i;
				break;
			}	
		}

 

 

배낭 알고리즘 자세한 내용은 좋은 글이 있어서 링크로 대체

https://gsmesie692.tistory.com/113

 

Dynamic Programming: 배낭 채우기 문제 (Knapsack Problem)

도둑이 보석가게에 배낭을 메고 침입했다. 배낭의 최대 용량은 W이며, 이를 초과해서 보석을 담으면 배낭이 찢어질 것이다. 각 보석들의 무게와 가격은 알고 있다. 배낭이 찢어지지 않는 선에서

gsmesie692.tistory.com

 

전체 코드는 아래 참고.

 

2) 시간복잡도

최대 시간복잡도 O(N*10,000)이나, 실제로 10,000 대신 cost 합계로 하는 경우 조금 더 줄어들것으로 예상.

(Java 기준 -  188ms)

 

3) 공간복잡도

메모리를 많이 쓰지는 않지만, dp [ i ] [ j ] 에서 j열을 어디까지 선언해줘야하는지 고민이 필요함.

(100*100)

 

4) 풀면서 놓쳤던점

특별히 없음.

 

5) 이 문제를 통해 얻어갈 것

배낭 (Knapsack) 알고리즘 코드 작성법.

 

Java 코드 : 

import java.io.*;
import java.util.*;

public class Main {

	static class info {
		int mem, cost;

		public info(int mem, int cost) {
			this.mem = mem;
			this.cost = cost;
		}

	}

	static int N, M, ans, costSum;
	// dp[i][j] : i번째 앱까지 확인헀을때 j cost를 소모해서 얻을 수 있는 최대 메모리
	static int[][] dp;        
	static info[] array;

	public static void main(String[] args) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		// 1. 입력 받기
		StringTokenizer st = new StringTokenizer (br.readLine()) ;
		
		N = Integer.parseInt(st.nextToken());
		M = Integer.parseInt(st.nextToken());
		
		int ans = 0;
		// dp[i][j] : i번째 앱까지 확인했을때, j cost를 소모해서 얻을 수 있는 최대 메모리
		int[][] dp = new int[N + 1][10001];		// 메모리 한계 100*100 = 10000
		
		array = new info[N+1];
		st = new StringTokenizer (br.readLine()) ;
		for (int i= 1; i<=N; i++) {
			array[i] = new info(Integer.parseInt(st.nextToken()),0);
		}
	
		costSum = 0;
		st = new StringTokenizer (br.readLine()) ;
		for (int i= 1; i<=N; i++) {
			array[i].cost = Integer.parseInt(st.nextToken());
			costSum += array[i].cost;
		}
		 
		// 2. 냅색 알고리즘 실행
		for (int i = 1; i <= N; i++)
		{
			for (int j = 0; j <= costSum; j++)
			{
				// 2-1. 갱신이 가능하면 둘 중 큰 값 넣어주기
				if (j - array[i].cost >= 0) {
					dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - array[i].cost ] + array[i].mem );
				}
				// 2-2. 아니면 원래 있던 값
				dp[i][j] = Math.max(dp[i][j], dp[i - 1][j]);
			}
		}

		// 3. 최소비용 확인
		for (int i = 0; i <= costSum; i++)
		{
			// M바이트 확보한 최소비용
			if (dp[N][i] >= M)
			{
				ans = i;
				break;
			}	
		}
	
		System.out.println(ans);
		
		br.close();
	}
}
반응형