알고리즘/배낭문제(DP)

BOJ 백준 10982 행운쿠키 제작소

kyjdummy 2025. 4. 10. 21:04

문제 : https://www.acmicpc.net/problem/10982

 

[ 문제 요약 ]

  • 두 오븐에 반죽들을 적절히 배분할 때, 두 오븐에서 동시에 작업한다고 가정했을 때, 전체 완료 시간(두 오븐의 작업 시간 중 큰 값)을 최소로 만드는 배분을 찾는 것

 

[ 테스트 케이스 설명 ]

2	// 테스트케이스 수
3	// 반죽의개수(1<=1000)
10 5	// 오븐 1,2에서 각각 구워지는 걸리는 시간(1<=100)
8 5
8 5
4
15 20
30 21
5 3
10 10
// 테스트 케이스마다 최소시간 출력
10
25

 

[ 문제 풀이 ]

 

반죽들을 두 오븐에 어떻게 분배하면, 두 오븐 중 작업 시간이 더 긴 쪽이 최소가 될까를 생각해야 합니다.

 

이를 생각하기 위해 문제 조건을 먼저 정리합니다.

 

 

[문제 조건 정리]

  • 각 반죽은 오븐 1에서 굽거나 오븐 2에서 굽는 둘 중 하나의 선택지가 있다.
  • 만약 어떤 반죽을 오븐 1에 배정하면, 그 반죽은 시간 a를 소요하고, 반대로 오븐 2에 배정하면 시간 b가 소요된다. 두 오븐의 처리 시간이 서로 상호 배타적이다.
  • 전체 작업이 끝나는 시간은 두 오븐이 동시에 작동할 때 두 오븐의 처리 시간 중 더 큰 값이 된다.

 

“어떤 반죽들을 오븐 1에 배정했을 때, 오븐 1의 총 소요시간은 얼마고, 동시에 그 반죽들로 인해 오븐 2에는 어떤 시간이 발생하는지를 계산할 수 있을까?”

 

라는 질문을 그대로 코드로 구현할 수 있습니다. 

 

두 오븐 처리 시간이 상호 배타적이기 때문에 전체 반죽에 대해, 오븐 1에 특정 반죽들을 배정한다면,

 

여러 배정에 따른 오븐 1의 총 시간은 여러 가지가 될 수 있고, 그리고 동시에 이런 배정들은 오븐 2에서는 얼마만큼의

 

시간을 절약하거나 추가로 필요한지를 결정하게 됩니다.

 

DP 배열의 인덱스를 [오븐 1의 사용 시간]으로 두고,

 

DP[X]는 [오븐 1에 X 분을 배정했을 때 오븐 2가 처리해야 하는 시간의 최솟값]으로 정의하면 이 문제를 풀 수 있습니다.

 

예를 들어

 

[오븐 1이 10분 걸려서 처리할 것을, 오븐 2에 배정하면 5분만 걸린다]

 

를 알 수 있게 오븐 1의 시간을 기준으로 오븐 2의 시간을 구합니다.

 

두 그룹으로 나누는 문제를 → 부분 집합 선택 문제로 모델링 하여 풉니다.

 

[ 정답 코드 ]

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
class Node{
	int a, b;
	Node(int a, int b){
		this.a=a;
		this.b=b;
	}
}
class Main{
	public static void main(String[] args)throws Exception{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringBuilder sb = new StringBuilder();
		int T = Integer.parseInt(br.readLine());
		while(T-->0)
		{
			int N		= Integer.parseInt(br.readLine());	//반죽의개수(1<=1,000)
			Node node[] = new Node[N];
			int sumA	= 0;
			
			for(int i=0; i<N; i++)
			{
				StringTokenizer st = new StringTokenizer(br.readLine());
				int a = Integer.parseInt(st.nextToken());// 오븐 1에서 구워지는 걸리는 시간(1<=100)
				int b = Integer.parseInt(st.nextToken());// 오븐 2에서 구워지는 걸리는 시간(1<=100)
				node[i] = new Node(a,b);
				sumA += a;
			}

			// dp의 인덱스의 의미 : 오븐 1에서 구울 때의 시간의 합
			// dp[x]안에 담긴 값의 의미 : x시간 동안 오븐 2에서 구울 때의 시간의 합 중 최소
			int dp[] = new int[sumA + 1];
			
			Arrays.fill(dp, 1<<30);
			
			dp[0] = 0;
			
			for(Node now : node)
			{
				// 오븐 1에서 구워지는 총시간을 역순으로 내려가면서 탐색
				for(int j=sumA; j>=now.a; j--)
				{
					// dp[j]안에는 j시간동안 오븐 2에서 구울 때의 시간의 합 중 최소 값이 들어가야 한다.
					dp[j] = Math.min(dp[j], dp[j-now.a] + now.b);
				}
			}
			int ans = 1<<30;
			
			for(int j=0; j<=sumA; j++)
				// 오븐 1의 시간의 총합이 j일 때, 오븐 2의 시간의 총합은 sumA - j 인덱스 안에 담긴 값이다.
				ans = Math.min(ans, Math.max(j, dp[sumA - j]));
			
			sb.append(ans).append('\n');
		}
		System.out.print(sb);
	}
}