
호텔에 투숙 고객을 늘리기 위해 n개의 도시에서 광고를 진행하려 한다.
각 도시의 광고에는
목표는 최소 비용으로 c명 이상의 고객을 유치하는 것이다.
단, 각 도시의 광고는 여러 번(무한히) 진행 가능하다.
이 문제는 전형적인 무한 배낭(Unbounded Knapsack) 문제다.
for j = value → dp.length) 순회하며 비용을 누적 계산해야 한다. 핵심 차이점:
for j = t → cost (역순) for j = cost → t (정순)dp[j]: j명의 고객을 유치하기 위해 필요한 최소 비용dp[0] = 0 INF (즉, 매우 큰 값)으로 초기화각 도시 (cost, value)에 대해,
j명 유치가 가능한 모든 경우를 순회하며 최소 비용 갱신.
dp[j] = min(dp[j], dp[j - value] + cost)
단, j >= value일 때 가능.
c명 이상을 달성하는 데 필요한 최소 비용을 찾는다.
즉,
min(dp[c], dp[c+1], dp[c+2], … ) 중 최소값이 답이다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int c = Integer.parseInt(st.nextToken()); // 목표 고객 수
int n = Integer.parseInt(st.nextToken()); // 도시 수
int[][] cityInfoArr = new int[n][2]; // {비용, 유치 고객수}
for (int i = 0; i < n; i++) {
st = new StringTokenizer(br.readLine());
cityInfoArr[i][0] = Integer.parseInt(st.nextToken());
cityInfoArr[i][1] = Integer.parseInt(st.nextToken());
}
int maxCost = 1000 * 100; // dp 배열 크기 설정
int[] dp = new int[maxCost + 1];
Arrays.fill(dp, maxCost);
dp[0] = 0;
for (int i = 0; i < n; i++) {
int cost = cityInfoArr[i][0];
int value = cityInfoArr[i][1];
for (int j = value; j < dp.length; j++) {
dp[j] = Math.min(dp[j], dp[j - value] + cost);
}
}
int minCost = maxCost;
for (int i = c; i < dp.length; i++) {
minCost = Math.min(minCost, dp[i]);
}
System.out.println(minCost);
}
}
예를 들어, C = 12, N = 3이고 각 도시 정보가 다음과 같을 때:
| 도시 | 비용 | 고객 수 |
|---|---|---|
| 1 | 3 | 5 |
| 2 | 1 | 1 |
| 3 | 4 | 7 |
for j = value → dp.length 정순 순회로 중복 가능하게 갱신. dp[c]가 아니라, c이상인 인덱스의 최소값이 정답.