본문 바로가기
PS/BOJ

[자바] 백준 7588 - Amicable (java)

by Nahwasa 2024. 2. 19.

목차

    문제 : boj7588

     

     

    필요 알고리즘

    • 수학, 정수론
      • 약수 구하는 것과 관련된 정수론 문제이다.

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

     

     

    풀이

      N이 최대 백만이고, 테스트케이스가 존재한다. 따라서 미리 백만까지의 모든 Amicable 쌍을 구해둔 후, 각 테스트케이스마다 알맞게 출력해주면 된다.

     

      여기서 구현이 필요한 부분은 결국 어떠한 수가 주어졌을 때, 그 수의 모든 제수의 합을 구하는 녀석이 필요하다. 이 때, 주어진 수의 제곱근까지만 확인해보면 되는데 그 이유는 '에라토스테네스의 체 혹은 소수판정 시 제곱근 까지만 확인하면 되는 이유' 글을 참고해보면 알 수 있다.

    public int sumOfDivisors(int n) {
        int limit = (int) Math.sqrt(n);
        int sum = 1;
        for (int i = 2; i <= limit; i++) {
            if (n%i==0) sum += i + (n/i);
        }
        if (limit * limit == n) sum-=limit;
        return sum;
    }

     

     

      Amicable 쌍을 미리 구성해두는 방법은, 그냥 100만 이하의 모든 수를 확인하면서 찾아보면 된다. 시간복잡도로 보면 O(100만*sqrt(100만)) 이라 오래걸릴 것 같지만, set으로 쳐낸 것도 있고해서 생각보다 그렇게 오래걸리지 않는다.

    private List<int[]> initAmicablePairs() {
        List<int[]> amicablePair = new ArrayList<>();
        Set<Integer> alreadyChecked = new HashSet<>();
    
        for (int i = 2; i <= LIMIT_OF_INPUT; i++) {
            if (alreadyChecked.contains(i)) continue;
    
            int a = sumOfDivisors(i);
            if (i >= a || a > LIMIT_OF_INPUT) continue;
    
            if (sumOfDivisors(a) == i) {
                amicablePair.add(new int[]{i, a});
                alreadyChecked.add(a);
            }
        }
        return amicablePair;
    }

     

     

      또, 사실 100만 이하에서 이렇게 찾을 수 있는 쌍이 40개도 안되기 때문에 그냥 미리 저거 돌린 후에 코드에 40개도 안되는 쌍을 미리 넣어두면 빠르게 동작하는 코드를 짤 수도 있다.

     

     

    코드 : github

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    
    public class Main {
        static BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1 << 16);
        private static final int LIMIT_OF_INPUT = 1000000;
    
        public static void main(String[] args) throws Exception {
            new Main().solution();
        }
    
        public void solution() throws Exception {
            List<int[]> amicablePair = initAmicablePairs();
    
            StringBuilder sb = new StringBuilder();
            while (true) {
                int n = Integer.parseInt(br.readLine());
                if (n == 0) break;
    
                sb.append('\n').append("Amicable numbers between 1 and ").append(n).append('\n');
                int cnt = 0;
                for (int[] cur : amicablePair) {
                    if (cur[1] <= n) {
                        sb.append(cur[0]).append(' ').append(cur[1]).append('\n');
                        cnt++;
                    }
                }
                if (cnt == 0) {
                    sb.append("None\n");
                }
    
            }
    
            System.out.print(sb);
        }
    
        private List<int[]> initAmicablePairs() {
            List<int[]> amicablePair = new ArrayList<>();
            Set<Integer> alreadyChecked = new HashSet<>();
    
            for (int i = 2; i <= LIMIT_OF_INPUT; i++) {
                if (alreadyChecked.contains(i)) continue;
    
                int a = sumOfDivisors(i);
                if (i >= a || a > LIMIT_OF_INPUT) continue;
    
                if (sumOfDivisors(a) == i) {
                    amicablePair.add(new int[]{i, a});
                    alreadyChecked.add(a);
                }
            }
            return amicablePair;
        }
    
        public int sumOfDivisors(int n) {
            int limit = (int) Math.sqrt(n);
            int sum = 1;
            for (int i = 2; i <= limit; i++) {
                if (n%i==0) sum += i + (n/i);
            }
            if (limit * limit == n) sum-=limit;
            return sum;
        }
    }

     

    댓글