본문 바로가기
알고리즘&코딩테스트

[알고리즘&코딩테스트] 매개 변수 탐색 (Parametric Search)

by 책 읽는 개발자_테드 2022. 1. 15.
반응형

매개 변수 탐색(Parametric Search)란?

· 이진 탐색(이분 탐색)을 사용하여 조건을 만족하는 최대값을 구하는 방법이다.

 

· 핵심

1. 정답을 매개 변수로 만들고, Yes/No 문제(결정 문제)로 바꿔 보기

2. 모든 값에 대해서 Yes/No를 채웠다고 생각했을 때, 정렬된 상태인가?

3. Yes/No를 결정하는 문제로 풀기

 

· 문제를 거꾸로 푸는 것이기 때문에 통찰력이 요구된다.

· 최근 코딩테스트 빈도로 굉장히 높게 나온다.

· 키워드에 "~~의 최댓값/최솟값 을 구하시오"가 포함되면 매개 변수 탐색을 접근해볼 가치가 있다. 

 

· 자주 하는 실수

1. 매개 변수에 대한 결정이 Nooooooo Yessssss 꼴이 아닌데 이분 탐색을 하는 경우

2. L, R, M, Result 변수의 정의를 헷갈려서 부등호 등을 잘못 쓰는 경우

3. L, R 범위를 잘못 설정하거나 Result의 초기값을 잘못 설정하는 경우

 

· 관련 문제

- BOJ 2805 - 나무 자르기

- BOJ 1654 - 랜선 자르기

- BOJ 2512 - 예산

- BOJ 2110 - 공유기 설치

- BOJ 2343 - 기타 레슨

- BOJ 6236 - 용돈 관리

- BOJ 13702 - 이상한 술집

- BOJ 17266 - 어두운 굴다리

- BOJ 1300 - K 번째 수 (고난이도)

- BOJ 1637 - 날카로운 눈 (고난이도)

 

예제 1

· BOJ 2805 - 나무 자르기 https://www.acmicpc.net/problem/2805

· 문제 파악

- 나무 개수 N = 100만, 필요한 나무 길이 M = 20억

- 잘린 나무의 길이 합 <= 나무 높이의 총합 = 100만 x10억 -> 즉, 계산 과정 중의 변수 타입은 long을 사용해야함

- 뒤집은 문제: 어떤 높이(H)로 잘랐을 때, 원하는 길이 M 만큼을 얻을 수 있는가?

· 시간 복잡도: O(NlogX)

1. H를 정해서 결정 문제 한 번 풀기 => O(N), 2. 정답의 범위를 이분 탐색하면서 풀기 =>  O(logX)번 반복

 

public class BOJ2805나무자르기 {
   static int N;
   static int M;
   static int[] trees;

   public static void main(String[] args) {
      input();
      pro();
   }

   private static void pro() {
      long H = 0; // 최대 절단기 높이
      long start = 0;
      long end = 2000000000;
      while (start <= end) {
         int mid = (int)((start + end) / 2); // 절단기 높이
         if (determination(mid)) {
            H = mid;
            start = mid + 1; // 더 큰 절단기 중에 조건을 만족하는 것이 있는지 탐색
         } else {
            end = mid - 1;
         }
      }

      System.out.println(H);
   }

   private static boolean determination(long H) {
      long sliced = 0; // 잘린 목재 길이
      for (int tree : trees) {
         if (tree > H) {
            sliced += tree - H;
         }
      }
      return sliced >= M;
   }

   private static void input() {
      Scanner sc = new Scanner(System.in);
      N = sc.nextInt();
      M = sc.nextInt();
      trees = new int[N];
      for (int i = 0; i < trees.length; i++) {
         trees[i] = sc.nextInt();
      }
   }
}

 

예제 2

· BOJ 2110 - 공유기 설치 https://www.acmicpc.net/problem/2110

· 문제 파악

- 가장 멀리 설치할 경우 정답은 10억 이하 -> Integer로 표현 가능

- 키워드 체크: "가장 인정합 두 공유기 사이의 거리를 최대로"하는 프로그램

- 매개 변수 만들기(뒤집은 문제): 

   - 원래 문제: C개의 공유기를 설치했을 때, 최대 인접 거리(D)는 얼마인가?

   - 뒤집은 문제: 어떤 거리 만큼 거리를 둘 때, 공유기 C개를 설치할 수 있는가? Yes/No

- 총 시간 복잡도: O(NlogN + NlogX)

   1. 주어진 집들을 정렬하기 O(NlogN)

   2.  D를 정해서 결정 문제 한 번 풀기 O(N)

   3. 정답의 범위를 이분 탁색하면서 풀기 O(logX)번 반복


import java.util.Arrays;
import java.util.Scanner;

public class BOJ2110공유기설치 {
   static int N, C;
   static int A[];

   public static void main(String[] args) {
      input();
      pro();
   }

   private static void input() {
      Scanner sc = new Scanner(System.in);
      N = sc.nextInt();
      C = sc.nextInt();
      A = new int[N+1];
      for (int i = 1; i < A.length; i++) {
         A[i] = sc.nextInt();
      }
   }

   static boolean determination(int D) {
      // D 만큼의 거리 차이를 둔다면 C개 만큼 공유기를 설치할 수 있는가?

      // 제일 왼쪽 집부터 가능한 많이 설치해본다.
      // D 만큼의 거리를 두면서 최대로 설치한 개수와 C를 비교한다.
      int cnt = 1, last = A[1];
      for (int i = 2; i <= N; i++) {
         // 이번에 A[i]에 설치가 가능한가?
         if (A[i] - last >= D) {
            cnt++;
            last = A[i];
         }
      }
      return cnt >= C;
   }

   static void pro() {
      // determination을 빠르게 하기 위해 정렬
      Arrays.sort(A);

      int L = 1, R = 1_000_000_000, ans = 0;
      // [L...R] 범위 안에 정답이 존재
      // 이분 탐색과 determination 문제를 이용해서 answer를 빠르게 구하자.
      while (L <= R) {
         int mid = (L + R) / 2;
         if (determination(mid)) {
            ans = mid;
            L = mid + 1;
         } else {
            R = mid - 1;
         }
      }
      System.out.println(ans);
   }
}
반응형

댓글