57. [Java] 전국 대회 선발 고사
https://school.programmers.co.kr/learn/courses/30/lessons/181851
- 학생은 총 n명, 번호는 0번부터 n - 1번까지 존재한다.
- 입력
- 각 학생의 선발 고사 등수는 rank[i]에 저장되어 있다.
- 각 학생의 전국 대회 참석 가능 여부는 attendance[i]에 저장되어 있다.
- 목표:
- 참석 가능한 학생들 중에서 등수가 높은 3명 선발
- 이 3명의 학생 번호를 각각 a, b, c,라고 할 때,
→ return 10000 * a + 100 * b + c
- rank[i] : i번 학생의 동수 (1~n 사이의 정수)
→ 등수는 중복 없이 모두 다름 - attendance[i] : i번 학생이 참석 가능한지 여부
- true : 참석 가능
- false : 참석 불가능
- 참석 가능한 학생은 최소 3명 이상 존재한다
정답코드
import java.util.*;
class Solution {
public int solution(int[] rank, boolean[] attendance) {
List<Integer> candidates = new ArrayList<>();
for (int i = 0; i < attendance.length; i++) {
if (attendance[i]) {
candidates.add(i); // 참가 가능한 학생의 번호(index) 저장하기
}
}
// 학생 번호 리스트를 rank 기준으로 정렬하기
// rank 값 기준으로 오름차순 정렬
// rank[i]가 작은 순서대로 → 1등. 2등, 3등...
// 만약 내림차순으로 정렬하고 싶으면 Comparator에 .reversed()붙이기
// candidates.sort(Comparator.comparingInt((Integer i) -> rank[i]).reversed());
// 중요포인트는 reversed()를 쓰기 위해선 (Integer i) 처럼 람다 파라미터의 타입을 명시해야 한다
// 타입 안 쓰면 컴파일 에러
candidates.sort(Comparator.comparingInt(i -> rank[i]));
int a = candidates.get(0); // 가장 높은 순위
int b = candidates.get(1);
int c = candidates.get(2);
return 10000 * a + 100 * b + c;
}
}
참가 가능한 학생 중 등수가 가장 높은 3명의 학생 번호(index)를 골라서 각각 a, b, c에 넣고 10000 *a + 100 * b + c를 계산하는 문제이다.
- 참가 가능한 학생 번호만 모으기
- attendance[i] == true인 경우에만 해당 학생의 번호(index)를 candidates 리스트에 저장한다.
- 후보 학생들을 등수(rank) 기준으로 정렬하기
- `candidates` 리스트를 `rank[i]` 값 기준으로 오름차순 정렬한다.
- 상위 3명 학생 번호 추출하기
- 정렬된 리스트에서 앞의 3명 (`0`, `1`, `2`번째) 학생 번호를 각각 `a`, `b`, `c`에 저장한다.
- 마지막으로 수식에 맞게 계산 후 결과 반환하기
오답
import java.util.*;
class Solution {
public int solution(int[] rank, boolean[] attendance) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < attendance.length; i++) {
if (attendance[i]) {
list.add(rank[i]); // x
}
}
Collections.sort(list);
int a = list.get(list.size() - 1); // x
int b = list.get(list.size() - 2); // x
int c = list.get(list.size() - 3); // x
return 10000 * a + 100 * b + c;
}
}
list에 추가하는 데이터
list.add(rank[i]); // x
여기서 rank[i]만 저장하고 있다. 이건 i번 학생의 등수이다. 즉, 등수 값만 모아서 정렬한 것이다. rank 배열에서 순위값(rank) 기준으로 정렬은 했지만, 그에 해당하는 학생 번호(index)를 반영하지 않아서 잘못된 결과가 나온다. 학생 번호 = 인덱스이므로 아래와 같이 수정했다.
list.add(i); // o
정렬 기준
Collections.sort(list); // x
그냥 list를 오름차순 정렬하는데 이렇게 해서는 안 된다. 왜냐하면 list에 담긴 건 학생의 번호이므로, Collections.sort(list);를 하면 학생 번호만 오름차순으로 정리한 결과가 된다. 따라서 학생번호 리스트를 rank기준으로 정렬해야 한다. 람다식을 써서 정렬가능하다.
list.sort(Comparator.comparingInt(i -> rank[i])); // o
rank 값이 가장 높은 숫자를 a로 쓰고 있음
Collections.sort(list)로 오름차순 정렬 후, a, b, c변수에 값을 할당하는 과정에서 실수를 했다. 랭크는 숫자가 낮을 수록 높은 점수인데 처음 작성한 코드는 랭크 숫자가 큰 3명을 가져와서 오답처리 됐다. 배열 인덱스를 맨 뒤부터 넣는게 아니라 앞에서부터 넣어야 한다. 왜냐하면 1,2,3 중에 1번이 숫자는 낮지만 제일 점수가 높기 때문이다. 순서대로 list.get(0), list.get(1), list.get(2)로 수정하니까 바로 해결됐다.
마지막으로 이건 다른 사람들 코드 참고하면서 느낀건데, 담긴 데이터의 내용이 잘 드러나게 변수명을 쓸 필요가 있다. 그동안 나는 ArrayList의 이름을 항상 list로 지었는데, 들어가는 데이터를 바로 떠올릴 수 있는 이름으로 바꿔주면 좋을 거 같다. 이 코드의 list에는 참가한 학생 번호가 들어가니까 참가자 라는 의미에서 candidates 로 해주면 좋았을 거 같다.