https://www.acmicpc.net/problem/2512
2512번: 예산
첫째 줄에는 지방의 수를 의미하는 정수 N이 주어진다. N은 3 이상 10,000 이하이다. 다음 줄에는 각 지방의 예산요청을 표현하는 N개의 정수가 빈칸을 사이에 두고 주어진다. 이 값들은 모두 1 이상
www.acmicpc.net
코드설명
파라매트릭 서치 문제입니다.
기존의 이진 탐색 문제와의 구현은 유사하지만, 개념의 차이가 존재합니다.
이진탐색과 파라매트릭 서치의 차이점에 대해 알아보겠습니다.
- 이진탐색은 정렬된 값들이 주어졋을때, 그 값들 중 원하는 값을 찾습니다. 이진탐색은 정렬된 데이터 위에서만 유효합니다.
- 파라메트릭 서치는 주어진 범위 내에서 원하는 값 혹은 조건에 일치하는 값을 찾아냅니다. 조건에 부합하는 값을 찾는 것이기에, 정렬된 데이터가 아니라도 상관없습니다.
주어진 최적화 문제를 파라매트릭 서치를 활용해서 풀기 위해 결정 문제로 바꾸어 보겠습니다.
최적화 문제 : 정해진 총액(M) 이하에서 최대 예산을 받기 위해서 설정해야하는 최대의 상한액(K)은 얼마인가?
결정문제 : 정해진 총액(M) 이하에서 최대 예산은 최대의 상한액(K) 이하가 되도록 할 수 있는가?
위의 조건들을 기반으로 작성합니다.
문제에서 제가 틀렸던 부분은, 총 예산을 끝시작점으로 잡은점입니다.
paramatricSearch(0, M); // M : 총 예산
대신에,
for(int i=0;i<N;i++) { arr[i] = Integer.parseInt(st.nextToken()); end = Math.max(end, arr[i]); }
end값을 주어진 원소값 중 최대값으로 설정해야합니다.
이러한 end값으로 처리해야하는 이유는,
현재 예산 값으로 설정할경우, 항상 sum 값이 middle 값보다 작기 때문에 계속해서 예산이 늘어납니다.
end값을 통해 주어진 원소값 중 최대값으로 설정하여 더 작은 값에서 최대값을 찾도록 설정해야만 올바르게 파라매트릭 서치가 진행됩니다.
주석으로 코드에 자세한 설명을 작성해보았습니다.
코드
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 int N, M, K = 0; public static int[] arr; public static int answer = 0; public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); arr = new int[N]; st = new StringTokenizer(br.readLine()); int end = 0; for(int i=0;i<N;i++) { arr[i] = Integer.parseInt(st.nextToken()); end = Math.max(end, arr[i]); } st = new StringTokenizer(br.readLine()); M = Integer.parseInt(st.nextToken()); paramatricSearch(0, end); } public static void paramatricSearch(int start, int end) { int left = 0; //배정예산 시작점 int right = end; //배정예산 끝점 while(left <= right) { int middle = ( left + right ) / 2; //배정예산 계산. int sum = 0; for(int i=0;i<N;i++) { if(middle >= arr[i]) { //배정예산이 더 크다면, arr[i]만큼만 사용하면 됩니다. sum += arr[i]; //사용한만큼 더합니다. }else if(middle < arr[i]) { //사용해야하는 금액이 더 크다면, sum += middle; //배정된 예산만큼만 사용합니다. } } // System.out.println("middle:"+middle+"sum:"+sum); //만약 사용금액이 M보다 작다면, 예산을 더 늘려도 됩니다. if(sum <= M) { left = middle + 1; } //만약 사용금액이 M보다 크다면, 예산을 줄여야야합니다. else if(sum > M) { right = middle - 1; } } System.out.println(right); } }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.*; public class Main { public static int N,M; public static int[] arr; public static int answer = 0; public static int start = 0, end = 0; public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); arr = new int[N]; st = new StringTokenizer(br.readLine()); for(int i=0;i<N;i++) { arr[i] = Integer.parseInt(st.nextToken()); // 여러 지방의 예산 요청 end = Math.max(end, arr[i]); } st = new StringTokenizer(br.readLine()); M = Integer.parseInt(st.nextToken()); paraMatricSearch(); System.out.println(end); } public static void paraMatricSearch(){ while(start <= end) { //각 예산들의 최대값을 구하는 점 int middle = (start + end) / 2; //평균 예산 answer = 0; //지방정부들에 줄 수 있는 예산의 합 for(int i=0;i<N;i++) { if(arr[i] > middle) answer += middle; //평균 예산보다 더 많은 예산을 요청한 경우 평균 예산만 분배 else answer += arr[i]; //평균 예산안의 범위에 있을 경우 지방정부가 요청한 만큼 줍니다. } if(answer <= M) { //예산요청의 양이 M보다 작거나 같을경우 최대값을 찾아야하니, 예산의 양을 더 크게하기 위해 start를 증가시킴, 작거나 같은경우로 처리해야 최대값을 구합니다. start = middle + 1; }else { //예산요청의 양이 M보다 클경우, 예산을 줄여야되니 줄입니다. end = middle - 1; } } } }
정답코드 3 입니다.
package Main; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.StringTokenizer; public class Main { public static int C, N, H, W, K, M; public static int answer = 0; public static int[] arr; public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); arr = new int[N]; st = new StringTokenizer(br.readLine()); int maxRequest = 0; for(int i=0;i<N; i++) { arr[i] = Integer.parseInt(st.nextToken()); maxRequest = Math.max(maxRequest, arr[i]); } st = new StringTokenizer(br.readLine()); M = Integer.parseInt(st.nextToken()); System.out.println(optimize(1, maxRequest)); } public static int optimize(int lo, int hi) { int left = lo - 1, right = hi + 1; while(left + 1 < right) { int mid = (left + right) / 2; if(decision(mid) == true) { left = mid; } else if(decision(mid) == false) { right = mid; } } return right - 1; } public static boolean decision(int maxBudget) { int ret = 0; for(int i=0; i<N; i++) { if(arr[i] >= maxBudget) { ret += maxBudget; }else { ret += arr[i]; } } return ret <= M; } }
'알고리즘 > binary_search' 카테고리의 다른 글
[백준] 1654 랜선 자르기 - 파라매트릭 서치(Paramatric Search) JAVA (0) | 2023.07.28 |
---|---|
[백준] 2805 나무 자르기 - 파라매트릭 서치(Paramatric Search) JAVA (0) | 2023.07.27 |
[백준] 19637 IF문 좀 대신 써줘 - 이분탐색(binary search) + 시간초과 JAVA (0) | 2023.07.27 |
[백준] 2512 예산 - 파라메트릭 서치(Paramatric Search) JAVA (0) | 2023.07.27 |
[백준] 2417 정수 제곱근 - 이분탐색(binary search) JAVA (0) | 2023.07.26 |