안녕하세요! 피피아노입니다 🎵
이번 포스팅에서는 프로그래머스 문제 중 과일 장수 문제에 대해서 정리를 해보려고 합니다.
그럼 바로 시작하겠습니다.
문제 설명
과일 장수가 사과 상자를 포장하고 있습니다. 사과는 상태에 따라 1점부터 k점까지의 점수로 분류하며, k점이 최상품의 사과이고 1점이 최하품의 사과입니다. 사과 한 상자의 가격은 다음과 같이 결정됩니다.
- 한 상자에 사과를 m개씩 담아 포장합니다.
- 상자에 담긴 사과 중 가장 낮은 점수가 p (1 ≤ p ≤ k)점인 경우, 사과 한 상자의 가격은 p * m 입니다.
과일 장수가 가능한 많은 사과를 팔았을 때, 얻을 수 있는 최대 이익을 계산하고자 합니다.(사과는 상자 단위로만 판매하며, 남는 사과는 버립니다)
예를 들어, k = 3, m = 4, 사과 7개의 점수가 [1, 2, 3, 1, 2, 3, 1]이라면, 다음과 같이 [2, 3, 2, 3]으로 구성된 사과 상자 1개를 만들어 판매하여 최대 이익을 얻을 수 있습니다.
- (최저 사과 점수) x (한 상자에 담긴 사과 개수) x (상자의 개수) = 2 x 4 x 1 = 8
사과의 최대 점수 k, 한 상자에 들어가는 사과의 수 m, 사과들의 점수 score가 주어졌을 때, 과일 장수가 얻을 수 있는 최대 이익을 return하는 solution 함수를 완성해주세요.
문제 이해
문제가 너무 길어서 핵심만 뽑아보자면
사과 점수와 개수를 기반으로 최대 이익을 계산하는 문제입니다.
- k: 사과의 최대 점수
- m: 한 상자에 담아야 할 사과의 개수
- score: 사과들의 점수 배열
이 문제에서 중요한 점은 점수가 높은 사과들로 최대한 구성해서 상자를 만들어야 최대 이익을 얻을 수 있다는 것, 그리고 m개 단위로 상자를 포장하고, 남는 사과는 버린다는 점입니다.
문제 풀기
점수가 높은 사과 뽑아내기
우선 제가 접근 했던 방법은 점수가 높은 사과를 뽑아내기 위해 배열을 내림차순으로 정렬을 했고 내림차순으로 정렬을 했을 때 특정 구간의 마지막 값이 해당 상자의 최소 점수가 된다는 점을 이용해서 문제를 풀었습니다.
먼저 변수를 하나 선언해서 내림차순으로 정렬한 사과 점수를 해당 변수에 넣어주고 최대 이익을 계산하기 위해 변수를 하나 더 선언해주었습니다.
let sortedScore = score.sorted(by: >)
var maxProfit = 0
계산하기
그리고 계산 과정은 for문과 stride를 이용해서 계산 과정을 구현하였습니다.
for문 범위 설정
상자 단위로 묶기 위해 stride(from: to: by:)를 사용하여 배열을 m개씩 묶어주었습니다
for i in stride(from: 0, to: sortedScore.count - m + 1, by: m) {
}
to: 부분을 처음에는 문제에 점수가 k까지로 나와있어서 아무 생각없이 k까지로 해주었지만 k는 사과 점수의 최대값을 나타는 값이고 to 매개변수는 점수의 최대값과는 상관없이, 반복의 종료 조건이기 때문에 k로 하면 안 된다는 것을 알고 to 매개변수를 sortedScore.count - m + 1로 지정해주었습니다. 이렇게 지정한 이유는 상자 단위로 점수를 묶을 수 있는 마지막 시작점까지로 해야 하기 때문인데 점수 배열에서 m 개씩 묶을 때 마지막으로 유효한 시작 인덱스를 구하는 공식으로
- 배열의 길이 n = score.count
- 마지막 유효한 시작점은 n - m 이며, 반복 종료는 그다음 인덱스여야 하므로(stride 특성상 to는 포함이 안 됨) n - m + 1
예를 들어 sortedScore.count = 7, m = 4 이라면
- 유효한 마지막 시작점: n - m = 7 - 4 = 3
- 반복 종료 조건: n - m + 1 = 7 - 4 + 1 = 4
- 상자 1: 인덱스 0, 1, 2, 3
- 4 개가 남아도 남는 인덱스 4, 5, 6 는 m 개를 채우지 못하므로 버림
다른 예시로 sortedScore.count = 10, m = 3 이라면
- 유효한 마지막 시작점: n - m = 10 - 3 = 7
- 반복 종료 조건: n - m + 1 = 10 - 3 + 1 = 8
- 상자 1: 인덱스 0, 1, 2
- 상자 2: 인덱스 3, 4, 5
- 상자 3: 인덱스 6, 7, 8
- 남는 인덱스 9 는 버림
즉, 상자를 완벽히 채울 수 있는 범위까지만 반복하기 위해 sortedScore.count - m + 1을 사용했습니다.
(stride 의 특성상 to 는 포함되지 않으므로, 실제로 반복을 n - m 까지 실행하려면 +1 이 필요합니다.)
상자 가격 계산
그리고 for문 안에 변수 하나를 선언해줘서 상자의 최소 점수를 계산하고 해당 변수 안에 그 값을 넣어줬습니다.
그리고 상자 가격을 리턴하기 위해 최소 점수 x 상자에 담긴 개수(m)을 아까 선언했던 maxProfit에 넣어주었습니다.
for i in stride(from: 0, to: sortedScore.count - m + 1, by: m) {
let minScore = sortedScore[i + m - 1]
maxProfit += minScore * m
}
그리고 마지막으로 maxProfit 변수를 리턴해주었습니다.
return maxProfit
전체 코드를 확인해보면
import Foundation
func solution(_ k:Int, _ m:Int, _ score:[Int]) -> Int {
var box = m
let sortedScore = score.sorted(by: >)
var maxProfit = 0
for i in stride(from: 0, to: sortedScore.count - m + 1, by: m) {
let minScore = sortedScore[i + m - 1]
maxProfit += minScore * m
}
return maxProfit
}
이렇게 구성되게 됩니다.
느낀 점
지금까지 풀던 문제보다 더 어려운 난이도긴 했지만 쉬운 편인데도 구조적으로 접근하지 않으면 꽤 복잡해진다는 것을 느낄 수 있었습니다.
특히 반복 종료 조건이나 남은 요소 처리 등 실수하기 쉬운 부분에서 작은 차이들이 최종 결과에 큰 영향을 줄 수 있다는 것을 확인하니 더욱 더 구조적으로 접근해야겠다는 생각이 들었습니다.
이 문제에서 배운 부분들을 통해 다음에는 조건 하나하나를 꼼꼼하게 확인하고 이유를 명확히 설명할 수 있도록 연습해야겠습니다.
감사합니다.
잘못된 내용이 있거나 더 좋은 내용 피드백은 언제나 환영합니다!
궁금하신 부분은 댓글로 질문 부탁드립니다!
'Apple > Swift' 카테고리의 다른 글
[Swift] 삼총사 문제 풀이 및 회고 (6) | 2024.11.30 |
---|---|
[Swift] stride와 enumerated 알아보기 (2) | 2024.11.24 |
[Swift] 제곱근 판별하기 (5) | 2024.11.16 |
[Swift] n의 배수 고르기 문제 회고 (5) | 2024.11.14 |
[Swift] 배열의 유사도 회고 (11) | 2024.11.08 |