본문 바로가기
PS/BOJ

[자바] 백준 17436 - 소수의 배수 (java)

by Nahwasa 2024. 2. 24.

목차

    문제 : boj17436

     

     

    필요 알고리즘

    • 포함 배제의 원리 (inclusion and exclusion)
      • 포함 배제의 원리로 풀 수 있는 문제이다.

    ※ 제 코드에서 왜 main 함수에 로직을 직접 작성하지 않았는지, 왜 Scanner를 쓰지 않고 BufferedReader를 사용했는지 등에 대해서는 '자바로 백준 풀 때의 팁 및 주의점' 글을 참고해주세요. 백준을 자바로 풀어보려고 시작하시는 분이나, 백준에서 자바로 풀 때의 팁을 원하시는 분들도 보시는걸 추천드립니다.

     

     

    풀이

      예제 입력 2를 생각해보자.

    2 100
    2 3

     

      직관적으로 우린 이걸 푸는 방법을 알고 있다. 100 이하의 자연수 중 2로 나누어 떨어지는 수는 100/2 = 50개가 존재한다. 그리고 3으로 나누어떨어지는건 100/3 = 33개가 존재한다. 그리고 둘이 중복으로 세어버린 100/(2x3) = 16개는 빼줘야한다. 최종적으로 50 + 33 - 16 = 67이 답이다.

     

      3개라면 어떨까? 주어진 소수가 A, B, C 라고 한다면

    100/A + 100/B + 100/C - 100/(AB) - 100/(BC) - 100/(AC) + 100/(ABC) 가 될 것이다.

    이걸 일반화해서 4개, 5개, ... 에 대해서도 정의한게 포함 배제의 원리이다. 결론적으로 그냥 홀수 개의 교집합은 더하고, 짝수 개의 교집합은 빼면 된다.

    long answer = 0l;
    for (int i = 1; i <= n; i++) {
        long tmp = solve(-1, i, 1);	// i개의 교집합으로 셀 수 있는 경우의 수
        
        if (i%2==1) answer+=tmp;	// 홀수 교집합이면 더하고
        else answer-=tmp;	// 짝수 교집합이면 뺀다
    }
    System.out.println(answer);

     

      개념적인 부분 보다는 재귀를 안쓰면 상당히 코드가 복잡해지므로 특정 x개의 교집합을 만드는 부분에서 구현이 좀 어려울 순 있다. 재귀가 익숙하지 않다면 디버깅을 걸어서 한번 어떤식으로 동작하는지 한 단계씩 생각해보자. 그리고 이 문제는 모든 입력값이 모두 다르고, 소수라서 별 문제 없었는데 그게 아니라면 좀 더 귀찮아진다.

    private long solve(final int bf, final int cnt, final long mult) {
        if (cnt == 0) {
            return m/mult;
        }
    
        long result = 0l;
        for (int i = bf+1; i <= n-cnt; i++) {
            result += solve(i, cnt-1, mult*arr[i]);
        }
        return result;
    }

     

     

     

     

    코드 : github

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.util.StringTokenizer;
    
    public class Main {
        private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1 << 16);
    
        public static void main(String[] args) throws Exception {
            new Main().solution();
        }
    
        private long[] arr;
        int n;
        long m;
    
        public void solution() throws Exception {
            StringTokenizer st = new StringTokenizer(br.readLine());
            n = Integer.parseInt(st.nextToken());
            m = Long.parseLong(st.nextToken());
            arr = new long[n];
            st = new StringTokenizer(br.readLine());
            for (int i = 0; i < n; i++) {
                arr[i] = Long.parseLong(st.nextToken());
            }
    
            long answer = 0l;
            for (int i = 1; i <= n; i++) {
                long tmp = solve(-1, i, 1);
                if (i%2==1) answer+=tmp;
                else answer-=tmp;
            }
            System.out.println(answer);
        }
    
        private long solve(final int bf, final int cnt, final long mult) {
            if (cnt == 0) {
                return m/mult;
            }
    
            long result = 0l;
            for (int i = bf+1; i <= n-cnt; i++) {
                result += solve(i, cnt-1, mult*arr[i]);
            }
            return result;
        }
    }

     

    댓글