문제 : https://www.acmicpc.net/problem/10023
[ 문제 요약 ]
- 들판마다 소의 음량을 측정하여 주어집니다. 들판마다의 음량을 구하는 식은 [ (이 전 들판에서의 음량 - 1) + 현재 들판에 있는 소들의 음량 ]입니다.
- 소들은 종류가 있고, 소들마다 고유의 음량이 다릅니다. 들판에 있는 소들의 예상 숫자 중 최소인 값을 출력합니다.
[ 테스트 케이스 설명 ]
5 2 // 들판 수(1<=100), 품종 수(1<=20)
5 // 품종에 따른 음량 수
7 // 품종에 따른 음량 수
0 // 이하 들판에서의 녹음된 소리
17
16
20
19
답 : 4
[ 문제 해설 ]
- 들판 하나하나마다 예상되는 소들의 최소수를 구해서 결과를 출력해 주면 됩니다. 이때 소의 수를 알 수 없는 경우 -1을 출력하고 바로 종료합니다.
- 현재 들판에 소가 만약 한 마리도 없는 경우는 연산을 스킵 합니다. 소가 한 마리도 없는 것은, 들판의 측청 음량 값 자체가 0이거나, 이전들 판의 음량에서 1을 뺀 값과 현재 들판 값이 같을 때입니다.
- 그리고 현재 들판에 소가 있다면, [이전 들판의 음량 - 1] 값보다 음량이 추가되는데, 추가된 음량만큼 계산해서, 그 음량을 만들기 위한 소들의 최솟값을 구해주면 됩니다.
- 소를 여러 마리 고를 수 있기 때문에 무한 배낭 문제입니다.
dp[음량] = 해당 음량을 만들 때 필요한 최소 소의 숫자
- 최소수를 구해야 하기 때문에 dp를 처음에 큰 값으로 초기화하여 사용합니다.
- 각 소들의 음량을 모두 사용해도, 해당 음량을 정확히 만들 수 없다면 -1을 출력하고 조기종료합니다.
아래는 정답 코드와 속도를 개선한 코드 2개를 보여줍니다.
[ 정답 코드 ]
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
class Main{
public static void main(String[] args)throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken());// 들판 수(1<=100)
int B = Integer.parseInt(st.nextToken());// 품종 수(1<=20)
int sound[] = new int[B + 1]; // 품종에 따른 소의 음량
int total[] = new int[N + 1]; // 각 들판마다 얻은 음량
for(int i=1; i<=B; i++)
sound[i] = Integer.parseInt(br.readLine()); // 1<=100
for(int i=1; i<=N; i++)
total[i] = Integer.parseInt(br.readLine()); // 0<=100,000
int ans = 0;
for(int i=1; i<=N; i++)
{
if(total[i] + 1 == total[i-1]) // 해당 들판에 소가 없을 경우
continue;
// 추가된 음량
int nowValue = total[i];
// 이전 들판 값이 0이 아닌 경우 해당 값의 -1 만큼 빼준다.
if(total[i-1] != 0)
nowValue -= (total[i-1] - 1);
// nowValue안에 소들이 최소로 들어가야 한다. 만약 불가한 경우 -1출력
// dp[음량] = 해당 음량에 가능한 최소 소의 숫자
int dp[] = new int[nowValue + 1];
Arrays.fill(dp, 1<<30);
dp[0] = 0;
// 무한 배낭 문제
for(int j=1; j<=B; j++)
for(int z=sound[j]; z<=nowValue; z++)
dp[z] = Math.min(dp[z], dp[z - sound[j]] + 1);
if(dp[nowValue] == 1<<30)
{
System.out.print(-1);
return;
}
ans += dp[nowValue];
}
System.out.print(ans);
}
}
[ 속도 개선 코드 ]
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
class Main{
public static void main(String[] args)throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
final int INF = 1<<30;
int N = Integer.parseInt(st.nextToken());// 들판 수(1<=100)
int B = Integer.parseInt(st.nextToken());// 품종 수(1<=20)
int sound[] = new int[B + 1]; // 품종에 따른 소의 음량
for(int i=1; i<=B; i++)
sound[i] = Integer.parseInt(br.readLine()); // 1<=100
int total[] = new int[N + 1]; // 각 들판마다 얻은 음량
for(int i=1; i<=N; i++)
total[i] = Integer.parseInt(br.readLine()); // 0<=100,000
int maxNeed = 0;
int need[] = new int[N + 1];
for(int i=1; i<=N; i++)
{
if(total[i] + 1 == total[i-1]) // 해당 들판에 소가 없을 경우
continue;
// 추가된 음량
int nowValue = total[i];
// 이전 들판 값이 0이 아닌 경우 해당 값의 -1 만큼 빼준다.
if(total[i-1] != 0)
nowValue -= (total[i-1] - 1);
need[i] = nowValue; // 결과적으로 구해야 하는 값을 저장해 나감
// 구해야하는 가장 큰 값 저장
maxNeed = Math.max(maxNeed, nowValue);
}
int dp[] = new int[maxNeed + 1];
Arrays.fill(dp, INF);
dp[0] = 0;
for(int s : sound)
for(int i=s; i<=maxNeed; i++)
dp[i] = Math.min(dp[i], dp[i - s] + 1);
int ans = 0;
for(int i=1; i<=N; i++)
{
if(dp[need[i]] == INF)
{
System.out.print(-1);
return;
}
ans += dp[need[i]];
}
System.out.print(ans);
}
}
[ 개선 코드 해설 ]
들판마다 dp를 실행하지 않고, 최종적으로 구해야하는 음량들을 모아놓고, 한번에 dp를 실행하는게 변화 포인트 입니다.
[ 속도 차이 ]
속도 차이가 2배 이상 나는 것을 볼 수 있습니다.
'알고리즘 > 배낭문제(DP)' 카테고리의 다른 글
BOJ 백준 31265 훈련 (1) | 2025.04.24 |
---|---|
BOJ 백준 14855 만두 가게 사장 박승원 (1) | 2025.04.17 |
BOJ 백준 2994 내한 공연 (0) | 2025.04.15 |
BOJ 백준 6066 Buying Hay (0) | 2025.04.14 |
BOJ 백준 5909 Bale Share (0) | 2025.04.14 |