배워서 남 주자

프로그래머스 Lv.1 | 과일장수 (JavaScript)

미래에서 온 개발자 2023. 5. 12. 12:24

지난주부터 몸풀기로 하루에 한 문제씩 코딩 테스트 연습 문제를 풀고 있다. 간단한 TIL을 매일 노션에 정리하고 있는데 블로그에 공유하고 싶은 내용이 있으면 간간히 포스팅을 해보려고 한다. 

 
문제 설명은 다음의 링크로 갈음한다. 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 
내 풀이

function solution(k, m, score) {
  score.sort((a, b) => b - a);

  const repeat = parseInt(score.length / m);
  let sum = 0;

  for (let i = m - 1; i < m * repeat; i += m) {
    sum += score[i];
  }
  return sum * m;
}

 
입출력 예 #2 로 내가 작성한 코드를 설명해 보겠다. 
k = 4,
m = 3,
score = [4, 1, 2, 2, 4, 4, 4, 4, 1, 2, 4, 2], 
result = 33
 
접근 방식:
score 배열을 내림차순으로 정렬한 다음, 배열에서 m만큼 끊어주면 한 상자에 담길 사과를 알 수 있다. 상자마다 마지막 요소의 합을 구한 뒤 m을 곱하면 최대 이익을 구할 수 있다. 
즉, [4, 4, 4, || 4, 4, 4, || 2, 2, 2, || 2, 1, 1] 에서 파란색으로 표시한 요소의 합 (4+4+2+1) * 3 = 33 이 최종 return 값이다. 
 
1) 변수 repeat : 반복 횟수, 즉 몇 개의 요소를 뽑아서 더할 것인지 
2) for loop :
  - 초기화: 변수 i는 m-1에서 시작
  - 조건문: repeat 횟수만큼 반복
  - 증감문: m만큼 증가하면서 반복
반복문 내에서 i는 m-1, m + (m-1), 2m + (m-1), ... , (x-1)m + (m-1)까지 총 repeat번 반복하게 된다. 
 
 
프로그래머스로 코딩 테스트 연습 문제를 매일 푸는 것 자체도 도움이 되지만 다른 사람의 풀이를 보면서 배우는 게 많다. 다른 사람의 풀이 두 가지를 가져와보겠다. 
 
다른 사람의 풀이 1 

solution = (_, m, s) => s.sort().filter((_, i) => !((s.length - i) % m)).reduce((a, v) => a + v, 0) * m

m번째로 잘리는 값을 가져와서 더한 뒤 m을 곱한다는 접근 방식은 나와 동일하지만, 이걸 filter와 reduce 메소드를 써서 한 줄로 끝내버린 풀이법이다. 
 
1) 제한 사항에 3 ≤ k ≤ 9 가 있기 때문에 sort()만으로 오름차순 정렬이 된다. 굳이 sort((a,b) => a-b) 를 해줄 필요가 없었다는 뜻이다. 
2) 위의 풀이에서 score.filter((_,i) => !((score.length - i) % m)) 부분이 한 번에 바로 이해가 잘 안됐다. 찬찬히 뜯어보니 나누어 떨어지지 않는 인덱스의 요소는 제거하고, 나머지가 0인 인덱스의 요소만 필터링하는 코드다. 
즉, [1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4] 배열이 있을 때 0번째, 3번째, 6번째, 9번째 요소를 뽑는 것이다. 
나는 남는 사과를 판별하기 위해서 내림차순 정렬을 한 다음 앞에서부터 끊어가는 방식을 선택했는데, 이 방식으로는 오름차순 정렬로도 충분히 같은 동작을 수행할 수 있다. 
 
 
다른 사람의 풀이 2

function solution(k, m, score) {
    let answer = 0;
    const sortedScore = score.slice().sort((a, b) => a - b).slice(score.length % m);
    for (let i = 0; i < sortedScore.length; i += m) {
        answer += sortedScore[i] * m;
    }
    return answer;
}

이것도 마찬가지 접근 방식이다. score 배열을 나중에 따로 사용하는 게 아니기 때문에 sortedScore를 구할 때 굳이 .slice()로 복사해서 score를 immutable하게 두지 않아도 되고, sort()만으로도 오름차순 정렬이 된다. 
.slice(score.length % m)을 통해서 배열 앞부분의 남는 사과를 버리고 for loop를 돌린 풀이법이다. 
 
 
코딩 테스트에서 다른 사람의 풀이법을 보면서 재밌는 두 가지 지점이 아예 다른 접근 방식을 볼 때와 똑같은 접근 방식으로 다른 코드를 작성할 때이다. 오늘은 후자의 경우였다. 프로그래밍적인 접근보다 수학적인 접근을 하게 되는 이런 문제를 풀 때면 수학의 정석 예제를 푸는 것 같은 기분이 든다.