ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [프로그래머스] 이모티콘 할인행사
    코딩 테스트 2025. 3. 24. 21:49

     

    https://school.programmers.co.kr/learn/courses/30/lessons/150368

     

    쌩구현 문제였다.

     

    [풀이한 코드]

    import java.util.*;
    
    class Solution {
            static final Map<Integer, Integer> percentMap = Map.of(10, 9, 20, 8, 30, 7, 40, 6);
        static final int[] pers = new int[]{10, 20, 30, 40};
        
        static int[][] users;
        static int[] emoticons;
        static int maxPlusUser;
        static long maxSale;
        
        public int[] solution(int[][] users, int[] emoticons) {        
            this.users = users;
            this.emoticons = emoticons;
            int[] percents = new int[emoticons.length];
            makePercents(0, percents);
            
            return new int[]{maxPlusUser, (int) maxSale};
        }
        
        private void makePercents(int cnt, int[] percents) {
            if (cnt == emoticons.length) {
                event(percents);
                return;
            }
            
            for (int i = 0; i < 4; i++) {
                percents[cnt] = pers[i];
                makePercents(cnt + 1, percents);
            }
        }
        
        private void event(int[] percents) {
            // 이모티콘 구매자
            List<Integer>[] emoticonBuyers = makeEmoticonBuyers(percents);
            
            // 할인된 이모티콘 가격
            int[] emoticonPrices = new int[percents.length];
            for(int i = 0; i < percents.length; i++) {
                emoticonPrices[i] = emoticons[i] / 10 * percentMap.get(percents[i]);
            }
            
            // 사용자별 구매 가격 계산 (이모티콘 플러스 고려 전)
            long[] totalPricesPerUsers = new long[users.length];
            for (int i = 0; i < percents.length; i++) {
                List<Integer> buyers = emoticonBuyers[i];
                for (int buyer : buyers) {
                    totalPricesPerUsers[buyer] += emoticonPrices[i];
                }
            }
            
            // 이모티콘 플러스 가입자 수
            boolean[] isPlusUser = new boolean[users.length];
            int plusUserCnt = 0;
            for (int i = 0; i < users.length; i++) {
                if (totalPricesPerUsers[i] >= users[i][1]) {
                    isPlusUser[i] = true;
                    plusUserCnt++;
                }
            }
            if (maxPlusUser > plusUserCnt) {
                return;
            }
            
            // 이모티콘 플러스 고려하여 판매액 계산
            long totalPrice = 0L;
            for (int i = 0; i < users.length; i++) {
                if (!isPlusUser[i]) {
                    totalPrice += totalPricesPerUsers[i];
                }
            }
            if (maxPlusUser < plusUserCnt) {
                maxPlusUser = plusUserCnt;
                maxSale = totalPrice;
            } else {  // maxPlusUser == plusUserCnt 인 경우
                maxSale = Math.max(maxSale, totalPrice);  
            }
        }
        
        static List<Integer>[] makeEmoticonBuyers(int[] percents) {
            List<Integer>[] emoticonBuyers = new ArrayList[percents.length];
            for (int i = 0; i < percents.length; i++) {
                int percent = percents[i];
                emoticonBuyers[i] = new ArrayList<>();
                for (int j = 0; j < users.length; j++) {
                    if (percent >= users[j][0]) {
                        emoticonBuyers[i].add(j);
                    }
                }
            }
            return emoticonBuyers;
        }
    }

     

     

    13, 15, 18번 테스트케이스에서 틀렸었다.

    부동 소수점연산은 근사값을 저장하기 때문에 이모티콘의 할인된 가격을 계산하면서 틀린 것이다.

     

    [기존 코드]

    static final Map<Integer, Double> percentMap = Map.of(10, 0.9, 20, 0.8, 30, 0.7, 40, 0.6);
    
    ```
    
    private void event(int[] percents) {
        ```
        // 할인된 이모티콘 가격
        int[] emoticonPrices = new int[percents.length];
        for(int i = 0; i < percents.length; i++) {
            emoticonPrices[i] = (int) (emoticons[i] * percentMap.get(percents[i]));
        }
        ````
    }

     

    위 코드에서 percentMap.get(percents[i]) 를 사용해서 곱셈을 하면 double 즉, 실수값이 반환된다. 하지만, 이는 결과값이 5400 이었어도 5400.0000001 이 저장되었을 수도 있다.

    그래서 모조리 정수 연산으로 바꾸어서 계산하도록 수정했다.

     

     

    [수정한 코드]

    static final Map<Integer, Integer> percentMap = Map.of(10, 9, 20, 8, 30, 7, 40, 6);
    
    ```
    
    private void event(int[] percents) {
        ```
        // 할인된 이모티콘 가격
        int[] emoticonPrices = new int[percents.length];
        for(int i = 0; i < percents.length; i++) {
            emoticonPrices[i] = emoticons[i] / 10 * percentMap.get(percents[i]);
        }
        ````
    }

     

    percentMap에 저장되는 타입을 <Integer, Integer> 으로 수정하고, emoticons[i] / 10 * percentMap.get(percents[i]) 처럼 정수만 사용하도록 수정했다.

     

     

     

    [기억하자]

    실수연산을 할 때는 부동소수점의 오류를 주의하자. 3.2 를 저장했어도 실제로는 3.20000000001 이 저장되어 추후 결과값이 완전히 달라질 수 있다.

    따라서 다음과 같은 해결책으로 부동소수점의 오류를 피하자.

    1. 최대한 정수로 연산하기
      • 10 * 0.8  ->  10 / 10 * 8  처럼 정수만을 사용하도록 수정
    2. Math.round(v)
      • Math.round() 를 해주면 소수점 첫째 자리까지 반올림해준다.

     

    그리고 이중 (혹은 그 이상의) for 문을 사용하고, 각각 인덱싱을 위한 변수로 쓰인다면 올바른 배열 혹은 리스트에 해당 인덱싱이 쓰였는지 조심!!하자.

Designed by Tistory.