https://school.programmers.co.kr/learn/courses/30/lessons/181916
프로그래머스의 주사위 게임 3의 자바 풀이법을 작성해 본다. 다른 블로그나 프로그래머스의 다른 사람의 풀이들을 봐도 명확하게 이해되지 않거나, 나름대로 어려운점이 있어서 HashMap 활용해서 풀이법을 작성한다.
⭐문제
1부터 6까지 숫자가 적힌 주사위가 네 개 있습니다. 네 주사위를 굴렸을 때 나온 숫자에 따라 다음과 같은 점수를 얻습니다.
- 네 주사위에서 나온 숫자가 모두 p로 같다면 1111 × p점을 얻습니다.
- 세 주사위에서 나온 숫자가 p로 같고 나머지 다른 주사위에서 나온 숫자가 q(p ≠ q)라면 (10 × p + q)2 점을 얻습니다.
- 주사위가 두 개씩 같은 값이 나오고, 나온 숫자를 각각 p, q(p ≠ q)라고 한다면 (p + q) × |p - q|점을 얻습니다.
- 어느 두 주사위에서 나온 숫자가 p로 같고 나머지 두 주사위에서 나온 숫자가 각각 p와 다른 q, r(q ≠ r)이라면 q × r점을 얻습니다.
- 네 주사위에 적힌 숫자가 모두 다르다면 나온 숫자 중 가장 작은 숫자 만큼의 점수를 얻습니다.
네 주사위를 굴렸을 때 나온 숫자가 정수 매개변수 a, b, c, d로 주어질 때, 얻는 점수를 return 하는 solution 함수를 작성해 주세요.
🚫제한 사항
a, b, c, d는 1 이상 6 이하의 정수입니다.
🪧입출력 예
a | b | c | d | result |
---|---|---|---|---|
2 | 2 | 2 | 2 | 2222 |
4 | 1 | 4 | 4 | 1681 |
6 | 3 | 3 | 6 | 27 |
2 | 5 | 2 | 6 | 30 |
6 | 4 | 2 | 5 | 2 |
💬입출력 예 설명
입출력 예 #1
예제 1번에서 네 주사위 숫자가 모두 2로 같으므로 1111 × 2 = 2222점을 얻습니다. 따라서 2222를 return 합니다.
입출력 예 #2
예제 2번에서 세 주사위에서 나온 숫자가 4로 같고 나머지 다른 주사위에서 나온 숫자가 1이므로 (10 × 4 + 1)2 = 412 = 1681점을 얻습니다. 따라서 1681을 return 합니다.
입출력 예 #3
예제 3번에서 a, d는 6으로, b, c는 3으로 각각 같으므로 (6 + 3) × |6 - 3| = 9 × 3 = 27점을 얻습니다. 따라서 27을 return 합니다.
입출력 예 #4
예제 4번에서 두 주사위에서 2가 나오고 나머지 다른 두 주사위에서 각각 5, 6이 나왔으므로 5 × 6 = 30점을 얻습니다. 따라서 30을 return 합니다.
입출력 예 #5
예제 5번에서 네 주사위 숫자가 모두 다르고 나온 숫자 중 가장 작은 숫자가 2이므로 2점을 얻습니다. 따라서 2를 return 합니다.
💫 풀이
- HashMap을 사용한 풀이법이다.
import java.util.HashMap; import java.util.Map;
최상단에 import를 빼먹지 않도록 유의하자. java.util 패키지 내에 있는 Map 인터페이스와 이를 구현한 HashMap 클래스를 사용했다. HashMap의 특징으로는 중복을 허용하지 않는다. 만약 동일한 key의 value를 넣게 된다면 이전에 가지고 있던 value는 사라지게 된다. 이를 사용하여 중복된 주사위의 수를 카운팅하고 조건에 맞는 수를 구할 수 있다.
- 전체 코드
import java.util.HashMap; import java.util.Map; class Solution { public int solution(int a, int b, int c, int d) { int score = 0; //== 동일한 주사위 수가 몇 번인지 계산하기 ==// int[] diceValues = {a, b, c, d}; Map<Integer, Integer> diceCount = new HashMap<>(); // HashMap은 중복을 허용하지 않는다. 따라서 동일한 값이 나오면 해당 key의 value가 1씩 증가하는 원리이다. for (int value : diceValues) { diceCount.put(value, diceCount.getOrDefault(value, 0) + 1); } // 모든 주사위가 동일한 값이 나올 경우 if (diceCount.containsValue(4)) { int commonValue = diceCount.entrySet().stream() .filter(entry -> entry.getValue() == 4) .map(Map.Entry::getKey) .findFirst() .orElse(0); return score = 1111 * commonValue; } // 주사위 3개가 동일한 값이 나올 경우 if (diceCount.containsValue(3)) { // 주사위 3개의 동일한 값 구하기 int commonValue = diceCount.entrySet().stream() .filter(entry -> entry.getValue() == 3) .map(Map.Entry::getKey) .findFirst() .orElse(0); // 주사위 1개의 값 구하기 int oneValue = diceCount.entrySet().stream() .filter(entry -> entry.getValue() == 1) .map(Map.Entry::getKey) .findFirst() .orElse(0); return score = (10 * commonValue + oneValue) * (10 * commonValue + oneValue); } // 주사위 2개씩 같은 값인 경우 if (diceCount.containsValue(2) && diceCount.size() == 2) { int[] pairValues = diceCount.entrySet().stream() .filter(entry -> entry.getValue() == 2) .map(Map.Entry::getKey) .mapToInt(Integer::intValue) .toArray(); return score = (pairValues[0] + pairValues[1]) * Math.abs(pairValues[0] - pairValues[1]); } // 주사위 2개는 같고 나머지 2개는 서로 다른 경우 if (diceCount.containsValue(2) && diceCount.containsValue(1)) { int[] otherValues = diceCount.entrySet().stream() .filter(entry -> entry.getValue() == 1) .map(Map.Entry::getKey) .mapToInt(Integer::intValue) .toArray(); return score = otherValues[0] * otherValues[1]; } // 주사위 모두가 다른 경우 if (diceCount.size() == 4) { return score = diceCount.keySet().stream() .mapToInt(Integer::intValue) .min() .orElse(0); } return score; } }
전체 코드는 위와 같다. 지금부터는 전체 코드를 하나씩 살펴보면서 어떻게 구현되어 있는지, 또 값은 어떻게 나오게 되는지 살펴보자.
✏️상세 풀이
class Solution {
public int solution(int a, int b, int c, int d) {
int score = 0;
//== 동일한 주사위 수가 몇 번인지 계산하기 ==//
int[] diceValues = {a, b, c, d};
Map<Integer, Integer> diceCount = new HashMap<>();
// HashMap은 중복을 허용하지 않는다. 따라서 동일한 값이 나오면 해당 key의 value가 1씩 증가하는 원리이다.
for (int value : diceValues) {
diceCount.put(value, diceCount.getOrDefault(value, 0) + 1);
}
가장 먼저 구해야 하는 것은 ‘동일한 주사위의 값이 몇 번이나 나오는가?’이다. 먼저 인수로 주어지는 주사위의 값을 int 배열로 만든다. 그리고 for each 문을 사용해서 배열에서 각 인덱스에 int 값을 꺼내오면서 계산한다.
HashMap은 중복을 허용하지 않는다는 특징이 있다. HashMap의 put()
메소드는 인자로 key와 value를 받고 key : value 형태의 데이터로 저장한다. key에는 주사위의 값이 value에는 동일한 주사위의 값에 대한 count 값을 저장한다.
HashMap의 getOrDefault()
메소드는 특정 key의 value가 존재하면 해당 value를 반환하고, key가 존재하지 않는다면( null이면 ) 0을 반환하는 메소드이다. 이 메소드를 사용한 이유는 이전에 한 번이라도 동일한 key 값이 있었다면 그 값을 유지한 채로 카운트를 올려야 하기 때문이다.
예를 들어 6, 6, 4, 3 라는 주사위의 값이 있다고 생각해보자. 제일 처음 6은 HashMap에 저장될 때, HashMap에 key가 6이 없으므로 getOrDefault()
메소드에서 0을 반환하고 +1 로 카운팅되어 6 : 1 형태로 저장된다.
key | value |
---|---|
6 | 1 |
두 번째 6은 기존의 HashMap에 6이라는 key가 있으므로 그 값인 value, 즉 1을 반환하고 +1 되어 key 6에는 2가 저장되게 된다. HashMap은 중복을 허용하지 않아 기존의 값을 덮어써 버린다.
key | value |
---|---|
6 | 2 |
세 번째 4는 기존의 HashMap에 4라는 key가 없다. 그래서 key 4에는 1이 저장된다.
key | value |
---|---|
6 | 2 |
4 | 1 |
네 번째 3는 기존의 HashMap에 3라는 key가 없다. 그래서 key 3에는 1이 저장된다.
key | value |
---|---|
6 | 2 |
4 | 1 |
3 | 1 |
이런 식으로 HashMap에 동일한 주사위에 대한 값을 카운팅하고, 주사위 값도 저장할 수 있다.
- 모든 주사위가 동일한 값일 경우
// 모든 주사위가 동일한 값이 나올 경우 if (diceCount.containsValue(4)) { int commonValue = diceCount.entrySet().stream() .filter(entry -> entry.getValue() == 4) .map(Map.Entry::getKey) .findFirst() .orElse(0); return score = 1111 * commonValue; }
가장 처음의 조건인 모든 주사위가 동일한 값일 경우이다. 앞에서 보았던 것처럼 만약 모든 주사위의 수가 6이라면 HashMap에는 아래와 같이 저장되어 있을 것이다.
key value 6 4 containsValue()
메소드는 HashMap 안에서 인수로 받은 value가 있는지 확인하고 true, false를 반환한다. 나머지 코드는 쉬우니까 생략하겠다. stream으로 바꿔서 filter와 map을 사용한 것밖에 없다. 어차피 모두 동일한 값이면 HashMap에서 그냥 꺼내오는 간단한 방법을 쓰면 되는 것이 아닌가?라고 생각될 수도 있는데 통일성을 위해서 이렇게 짰다.
- 주사위 3개가 동일한 값이 나올 경우
// 주사위 3개가 동일한 값이 나올 경우 if (diceCount.containsValue(3)) { // 주사위 3개의 동일한 값 구하기 int commonValue = diceCount.entrySet().stream() .filter(entry -> entry.getValue() == 3) .map(Map.Entry::getKey) .findFirst() .orElse(0); // 주사위 1개의 값 구하기 int oneValue = diceCount.entrySet().stream() .filter(entry -> entry.getValue() == 1) .map(Map.Entry::getKey) .findFirst() .orElse(0); return score = (10 * commonValue + oneValue) * (10 * commonValue + oneValue); }
코드 상 어려운 것은 없다. 주의해야할 점은 계산된 값의 제곱으로 반환해야한다는 것이었다.
- 주사위 2개씩 같은 값인 경우
// 주사위 2개씩 같은 값인 경우 if (diceCount.containsValue(2) && diceCount.size() == 2) { int[] pairValues = diceCount.entrySet().stream() .filter(entry -> entry.getValue() == 2) .map(Map.Entry::getKey) .mapToInt(Integer::intValue) .toArray(); return score = (pairValues[0] + pairValues[1]) * Math.abs(pairValues[0] - pairValues[1]); }
조건에서
size()
메소드가 사용되었다. 이는 반드시 동일한 값이 2쌍이 되어야하기 때문이다. 다음 조건에서 2개의 주사위 수가 동일하고 나머지 2개의 주사위 수는 서로 다른 조건이 있기 때문이다.Math.abs()
메소드는 절대값을 반환하는 메소드이다.
- 주사위 2개는 같고 나머지 2개는 서로 다른 경우
if (diceCount.containsValue(2) && diceCount.containsValue(1)) { int[] otherValues = diceCount.entrySet().stream() .filter(entry -> entry.getValue() == 1) .map(Map.Entry::getKey) .mapToInt(Integer::intValue) .toArray(); return score = otherValues[0] * otherValues[1]; }
- 주사위 모두가 다른 경우
if (diceCount.size() == 4) { return score = diceCount.keySet().stream() .mapToInt(Integer::intValue) .min() .orElse(0); }
여기까지 프로그래머스의 주사위 게임 3을 풀어보았다. if 문을 장황하게 작성하기 보다 다른 방법은 없을까 고민하면서 풀어보았다. 아직 많이 부족하지만 조금 더 간결하고, 더 직관적이게 코드를 작성해보고 싶다.
Uploaded by N2T