코딩 테스트
[프로그래머스] 이모티콘 할인행사
한 면만 쓴 종이
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 이 저장되어 추후 결과값이 완전히 달라질 수 있다.
따라서 다음과 같은 해결책으로 부동소수점의 오류를 피하자.
- 최대한 정수로 연산하기
- 10 * 0.8 -> 10 / 10 * 8 처럼 정수만을 사용하도록 수정
- Math.round(v)
- Math.round() 를 해주면 소수점 첫째 자리까지 반올림해준다.
그리고 이중 (혹은 그 이상의) for 문을 사용하고, 각각 인덱싱을 위한 변수로 쓰인다면 올바른 배열 혹은 리스트에 해당 인덱싱이 쓰였는지 조심!!하자.