티스토리 뷰
https://school.programmers.co.kr/learn/courses/30/lessons/150368
간만에 이런 식으로 문제를 풀다보니 생각보다 재밌네요.
이번에는 다른 문제를 가져왔습니다.
마찬가지로 카카오에서 나온 문제이고요
저번 Lv.1 에 비하면 확실히 생각할 부분이 많습니다.
천천히 같이 풀어봅시다.
문제 상황
카카오에서 이모티콘을 무제한으로 사용할 수 있는 서비스의 가입자를 늘리려고 합니다.
이번 이벤트의 최종 목표는 다음과 동일합니다.
1. 이모티콘 플러스 서비스 가입자를 최대로 늘리는 것
2. 이모티콘 판매액을 최대로 하는 것
1번 목표가 최우선이고 2번은 그 다음 입니다.
n명의 사용자에게 이모티콘 m개 할인행사를 진행합니다.
각 이모티콘의 할인률은 10%,20%,30%,40% 중 하나로 진행됩니다.
각 사용자들은 자신의 기준보다 많이 할인하는 이모티콘은 무조건 삽니다.
단 이때 총 구매금액이 자신의 기준 금액이상이면 모든 구매를 취소하고 무제한 서비스에 가입합니다.
이때 사용자들의 구매 기준(users)과 이모티콘들의 정가(emoticons)가 주어질때 최대 가입자수와 이모티콘 매출액을
1차원 정수 배열 ([Int]) 로 출력하세요.
정답 예시
users | emoticons | result |
[[40, 10000], [25, 10000]] | [7000, 9000] | [1, 5400] |
[[40, 2900], [23, 10000], [11, 5200], [5, 5900], [40, 3100], [27, 9200], [32, 6900]] | [1300, 1500, 1600, 4900] | [4, 13860] |
* 추가 설명
- 첫번째 정답을 좀 더 설명하면 각 이모티콘이 할인률이 30%(7000원),40%(9000원) 할인할때 최대 효율이라고 하면
사용자 | 구매 이모티콘 | 이모티콘 구매비용 (할인 적용) | 서비스 가입 여부 |
[40,10000] | 9000원 | 5400원 | X |
[25,10000] | 7000원,9000원 | 10300원 | O |
- 따라서 가입자 1명에 이모티콘 판매수익은 5400원이 됩니다.
문제 풀이 (swift)
저번과 마찬가지로 단계를 나누어 풀어보겠습니다.
1. 이모티콘 할인률 적용 경우의 수 구하기
2. 할인률 별 사용자들의 이모티콘 구매 금액과 서비스 가입자 수 구하기
3. 가장 많은 서비스 가입을 하는 경우와 금액 구하기
1번이 제가 보기엔 제일 메인 미션인 것 같아요.
제 생각에 이 문제의 포인트는
각 이모티콘을 얼마나 할인해야 최대 효율인지 스스로 알아내야한다는 점 입니다.
1. 이모티콘 할인률 적용 경우의 수 구하기
먼저 m개의 이모티콘을 각각 얼마나 할인할지를 나타내는 배열을 만들꺼에요
근데 이걸 모든 경우의 수를 구해야 가장 좋은 경우의 수를 구할 수 있겠죠?
* 추가 설명
- 사실 이 문제는 말이 어려울 뿐 경우의 수에서는 자주 보이는 문제와 동일합니다.
- [10%,20%,30%,40%] 를 순서대로 m개 나열하시오 랑 같은 말입니다.(중복 사용 가능)
- 다른 말로는 중복 순열이라 합니다.
- 이 부분의 원리는 시간이 되면 따로 올려보겠습니다.
좀 더 쉽게 이야기 해보면 같은 구조의 반복이라 할 수 있습니다.
반복 -> 재귀로 표현하면 좀 더 쉽겠죠?
Tip. m이 1개이거나 2개일때 정도만 생각해서 만드시면 좀 더 쉽습니다.
func getSalesArray(_ length : Int,_ count : Int) -> [[Int]] {
let caseSample = [10,20,30,40]
if(count == 1){
return [[10],[20],[30],[40]] // 한개인 경우는 별도 계산 X
}else{ // 이전 배열에 각각 하나씩 추가한 형태로 만들기
let lastArray = getSalesArray(length, count-1)
var newArrays : [[Int]] = []
for i in lastArray {
for j in caseSample{
var newElement = i
newElement.append(j)
newArrays.append(newElement)
}
}
return newArrays
}
}
실제로 저 함수를 돌려보면 아래처럼 나오게 됩니다.
print(getSalesArray(3, 3)) // 3개짜리로 만드시오
2. 할인률 별 사용자들의 이모티콘 구매 금액과 서비스 가입자 수 구하기
여기부터는 이제 좀 쉽습니다.
users, emoticons, 그리고 위에서 구한 경우의 수 중 한가지(sales)를 넣었을때
총 가입자 수와 금액을 구해주는 함수를 만들면 됩니다.
그럼 각각 유저 한명의 가입여부 + 구매 금액부터 알아야겠죠?
유저 한명의 가입 여부+ 구매 금액 (ex. 앞 자리가 1이면 가입 / 0이면 미가입)
// 개인 사용자 가입 여부 및 총 금액
func getPersonValue(_ user : [Int] ,_ emoticons:[Int],_ sales:[Int]) -> [Int] {
var total = 0
for i in 0..<sales.count{
if(sales[i] >= user.first!){
total += emoticons[i] * (100 - sales[i]) / 100
if(total >= user.last!){
// 기준 금액이 넘어가는 순간 더이상의 계산은 불필요하다.
return [1,0]
}
}
}
if(total >= user.last!){
return [1,0]
}
return [0,total]
}
이제 개인 가입 여부와 금액을 알아냈으니 합산해볼까요?
합산은 간단히 for 문을 이용해 줍시다.
// 각 케이스별 가입자수 및 전체 금액
func getCaseResult(_ users:[[Int]],_ emoticons:[Int],_ sales:[Int]) -> [Int] {
// sales : 할인 퍼센트
var totalSales = 0 // 전체 판매 금액
var totalSign = 0 // 전체 가입자 수
for i in users{
let userValue = getPersonValue(i, emoticons, sales)
if userValue.first! > 0 {
totalSign += 1
}else{ // 가입 안함 -> 금액 합산
totalSales += userValue.last!
}
}
return [totalSign,totalSales] // [가입자수,판매수익]
}
3. 가장 많은 서비스 가입을 하는 경우와 금액 구하기
자 이제 끝이 눈 앞에있습니다. 비교문만 돌리면 되요.
func solution(_ users:[[Int]], _ emoticons:[Int]) -> [Int] {
let salesPercents = getSalesArray(emoticons.count, emoticons.count)
var resultCase = [0,0]
for i in salesPercents {
let now = getCaseResult(users, emoticons, i) // [총 가입자수,판매수익]
if(now.first! == users.count){//사용자 전원 가입
return now // 최적의 상황이기에 더이상 할필요 X
}
if(now.first! > resultCase.first!){ // 가입자수가 저장된 경우보다 많은 경우
resultCase = now
}else if(now.first! == resultCase.first!){ // 가입자수 동일
if(now.last! > resultCase.last!){ // 판매 수익 비교
resultCase = now
}
}
}
return resultCase
}
사실상 경우의 수를 구하는 부분을 제외하면
많이 어려운 부분은 없는 문제입니다.
천천히 풀면 생각보다 쉽게 풀 수 있습니다.
이제 정리해봅시다.
최종 코드
import Foundation
func solution(_ users:[[Int]], _ emoticons:[Int]) -> [Int] {
let salesPercents = getSalesArray(emoticons.count, emoticons.count)
var resultCase = [0,0]
for i in salesPercents {
let now = getCaseResult(users, emoticons, i)
if(now.first! == users.count){
return now // 최적의 상황이기에 더이상 할필요 X
}
if(now.first! > resultCase.first!){
resultCase = now
}else if(now.first! == resultCase.first!){
if(now.last! > resultCase.last!){
resultCase = now
}
}
}
return resultCase
}
// 전체 할인 경우의 수 구하기
func getSalesArray(_ length : Int,_ count : Int) -> [[Int]] {
let caseSample = [10,20,30,40]
if(count == 1){
return [[10],[20],[30],[40]]
}else{
let lastArray = getSalesArray(length, count-1)
var newArrays : [[Int]] = []
for i in lastArray {
for j in caseSample{
var newElement = i
newElement.append(j)
newArrays.append(newElement)
}
}
return newArrays
}
}
// 각 케이스별 가입자수 및 전체 금액
func getCaseResult(_ users:[[Int]],_ emoticons:[Int],_ sales:[Int]) -> [Int] {
// sales : 할인 퍼센트
var totalSales = 0 // 전체 판매 금액
var totalSign = 0 // 전체 가입자 수
for i in users{
let userValue = getPersonValue(i, emoticons, sales)
if userValue.first! > 0 {
totalSign += 1
}else{ // 가입 안함
totalSales += userValue.last!
}
}
return [totalSign,totalSales]
}
// 개인 사용자 가입 여부 및 총 금액
func getPersonValue(_ user : [Int] ,_ emoticons:[Int],_ sales:[Int]) -> [Int] {
var total = 0
for i in 0..<sales.count{
if(sales[i] >= user.first!){
total += emoticons[i] * (100 - sales[i]) / 100
if(total >= user.last!){
// 넘어가는 순간 더이상의 계산은 불필요하다.
return [1,0]
}
}
}
if(total >= user.last!){
return [1,0]
}
return [0,total]
}
실제로 아래 함수를 돌려보면 문제에서 요구한 답변과 동일한 값을 얻을 수 있습니다.
print(solution([[40, 10000], [25, 10000]], [7000, 9000]))
print(solution([[40, 2900], [23, 10000], [11, 5200], [5, 5900], [40, 3100], [27, 9200], [32, 6900]], [1300, 1500, 1600, 4900]))
처음에는 이게 무슨 문제인지 감이 안왔는데
천천히 해보니까 생각보다 쉽게 풀리네요.
연습하시는 분들에게도 좋은 문제인 것 같아요.
오늘도 파이팅입니다.
'iOS개발 > 프로그래머스 연습' 카테고리의 다른 글
프로그래머스 개인정보 수집 유효기간 (Lv.1) 문제풀이 (0) | 2023.09.18 |
---|---|
순열 과 중복 순열 만들기 (with swift) (0) | 2023.08.24 |
프로그래머스 성격유형검사(Lv. 1) 문제풀이 (0) | 2023.08.11 |