1 - 1. Binary Search 예제 (백준 1072번: 게임)
이분 탐색의 예제로 풀어볼 문제입니다.
실제로 이분 탐색을 대상으로 문제가 나오기보단 대부분 Parametric Search 형태로 문제가 출제됩니다.
해당 문제는 Parametric Search로 보시는게 맞다고 생각합니다만, 좀 더 개념확립이 된다면 이 둘에 대한 차이에 대해서도 포스팅을 진행하겠습니다.
정답율이 꽤 낮은편인데도 불구하고 문제해결 아이디어는 생각보다 잘 떠오른 편이라 이 문제를 가져왔습니다.
사실 문제 자체가 분류가 안되어있다면, 이분 탐색이 익숙치 않다면 조금 헤맬 수 있는 문제라서 마냥 쉽다고 보긴 어려울 것 같습니다.
우선 아래의 두 조건에 대해 생각해 볼게요.
조건 1
앞으로의 모든 게임에서 지지 않는다
지지 않는 다는 것은 '이기는 것만 고려한다.'라는 뜻입니다.
만약 이 조건이 없었다면, 이기는 경우, 지는 경우 모두 고려했어야 했겠죠.
결국 주어진 X에서 어떤 값(판수)을 더한다면 Y에도 같은 값을 더하면 된다는 얘기입니다.
조건 3
형택이가 게임을 몇 판 더해야 Z가 변하는지 구하는 프로그램
- '몇판을 더 해야 Z가 변하는가?' 저는 이 조건이 조금 애매하다 생각하지만, 승률이 바뀔 수 있는 최소 판수를 구하라고 이해했습니다.
- 즉, 위의 조건 1과 함께 지지 않으면서 승률이 바뀌는 경우: 승률을 올릴 수 있는 최소 판수를 찾으라는 의미입니다.
- 만약 조건 1이 없었다면, 승률이 떨어지는 경우와 오르는 경우 두가지로 나누어 풀고 그때의 최솟값을 찾았어야 하겠죠.
전체 시행 수 = X
사실 뭐 이보다 더 나은 값이 있지 않을까 싶긴한데.. 제 능력 밖인거 같아요. ㅎㅎ
아무튼 증명도 되었으니 코드를 짜봅시다.
기본적인 동작은 이분 탐색만 실시하고 탐색 중간에 승률을 뽑아주는 메소드를 생성해서 계속 비교했습니다.
이 메소드는 getWinRate 라고 이름 지었구요.
소수점 자리는 무시한다 했으므로 Y(승리 횟수) * 100 / X (게임 판수)를 반환하게 했습니다.
원래 식은 Y / X * 100 인데, 이렇게 계산하면 Y는 항상 X보단 작게 되잖아요.
그래서 0이라는 결과를 반환하기 때문에 저런식으로 처리했습니다.
또한 Y가 최대 10억이고 100을 곱하면 int 범위를 초과하므로 마음 편하게 그냥 모두 long으로 잡아주었습니다.
우선 이분 탐색 메소드 부터 보여드리겠습니다.
1072번 Binary Search 메소드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | private static int binarySearch(long x, long y, long target) { int start = 1, end = (int) x, result = 0; if(getWinRate(x + x, y + x) == target) return -1; // 승률이 안오르는 경우 while(start <= end) { int mid = (start + end) / 2; long z = getWinRate(x + mid, y + mid); // 승률이 오른건지 체크 if(z > target) { end = mid - 1; result = mid; } else { start = mid + 1; } return result; } } | cs |
1 2 3 | private static long getWinRate(long x, long y) { return y * 100 / x; } | cs |
승률 계산 메소드는 위에서 설명 드린것과 동일하게 입력했습니다.
Boj1072.java 전체 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.StringTokenizer; /** * * @author minchoba * 백준 1072번: 게임 * * @see https://www.acmicpc.net/problem/1072/ * */ public class Boj1072 { public static void main(String[] args) throws Exception{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); long X = Integer.parseInt(st.nextToken()); long Y = Integer.parseInt(st.nextToken()); long winRate = getWinRate(X, Y); System.out.println(binarySearch(X, Y, winRate)); } private static int binarySearch(long x, long y, long target) { int start = 1, end = (int) x, result = 0; if(getWinRate(x + x, y + x) == target) return -1; // 승률이 안오르는 경우 while(start <= end) { int mid = (start + end) / 2; long z = getWinRate(x + mid, y + mid); // 승률이 오른건지 체크 if(z > target) { end = mid - 1; result = mid; } else { start = mid + 1; } } return result; } private static long getWinRate(long x, long y) { return y * 100 / x; // 소수화 방지 } } | cs |
채점 결과
아래 제출은 원래 제가 처음에 증명하기전에 쓴 탐색 범위를 무조건 +10억으로 하고 나온 결과입니다.
시간 복잡도 자체가 작기 때문에 결과에서도 별 차이가 없네요.
이해안가는 것이나, 질문 또는 잘못된 부분은 댓글로 지적 부탁드려요. 감사합니다.
문제의 저작권은 백준 온라인 저지에 있습니다.