코딩 테스트

[프로그래머스] 이모티콘 할인행사

한 면만 쓴 종이 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 문을 사용하고, 각각 인덱싱을 위한 변수로 쓰인다면 올바른 배열 혹은 리스트에 해당 인덱싱이 쓰였는지 조심!!하자.