https://school.programmers.co.kr/learn/courses/30/lessons/150368
n
명의 유저에게 m
개의 이모티콘을 할인해서 판매하는 행사를 함.조건 1
: 유저들은 일정 비율 이상 할인하는 이모티콘들을 모두 구매하고, 그 구매 가격이 일정 가격을 넘을 경우 이모티콘을 무제한으로 쓸 수 있는 이모티콘 플러스에 가입한다고 가정예를 들어 유저 1과 2는 다음과 같을 수 있음
비율 | 가격 | |
---|---|---|
1 | 40 | 10000 |
2 | 25 | 9000 |
유저 1은 할인율이 40% 이상인 모든 이모티콘을 구매하고, 그 구매 가격 총합이 10000을 넘기면 이모티콘 플러스에 가입을 한다.
유저 2는 할인율이 25% 이상인 모든 이모티콘을 구매하고, 그 구매 가격 총합이 9000을 넘기면 이모티콘 플러스에 가입을 한다.
조건 2
: 각 이모티콘의 할인율은 10, 20, 30, 40% 중 하나로 설정할 수 있고, 서로 다를 수 있음행사 목표
: 이모티콘 플러스 가입자를 늘리고(1), 이모티콘 판매액을 늘리는 것(2). (1 > 2)answer
: 행사 목표를 최대한 달성했을 때 이모티콘 플러스 가입자 수와 이모티콘 판매액예 : (10, 10, 10) ↔ (10, 20, 10)
⇒ 그래프로 표현 가능함
answer
을 찾으려면 가능한 이모티콘 할인율를 모두 찾아보면서 최대값을 확인해야 함 ⇒ 그래프 탐색으로 생각할 수 있음이모티콘 할인율을 조정할 때마다 모든 유저에 대해서 더하고 구독 가격을 넘겼는지 확인하는 방식으로 코드를 짜면 너무 오래 걸린다.
⇒ 할인율 확인을 반복하지 않기 위해 users
를 테이블(2중 리스트)로 구조를 바꾸자
⇒ 비슷한 할인율을 가진 user들의 emoticon 구입 총액은 같으니 그냥 4차원 벡터에 저장하자
⇒ users
전체에서 구독 비용을 넘었는지 확인하지 말고 테이블 가로 끝부터 확인하자
Ind | |||
---|---|---|---|
0 | 5900 | ||
1 | 5200 | ||
2 | 9200 | 10000 | |
3 | 2900 | 3100 | 6900 |
10%~ | 20%~ | 30%~ | 40%~ |
---|---|---|---|
2000 | 3000 | 3000 | 3500 |
연산 횟수 ≤ 4 ** 7 (할인율 경우의 수) X 유저의 수 (최대 100명)
table <- 테이블(2중 리스트).
table[i]에는 할인율 (i) * 10% ~ (i+1) * 10%의 user들이 정렬되어 저장
def bt(s = [0, 0, 0, 0], change = 0):
s <- 할인율 별 이모티콘 구입 총액
change <- 할인율을 설정할 이모티콘 index
answer <- 기존의 정답
if change == len(emoticons): # 마지막 이모티콘까지 할인율을 설정했다면
s로 테이블 가로 끝부터 탐색하면서 new_answer를 계산
answer = max(answer, new_answer)
return
for sale in range(1,5): # 1 -> 10%, 2 -> 20% ...
new_s = s.copy()
for i in range(sale):
# 만약에 sale이 3이면 index 3(40%)에서는 값을 추가 안 함
new_s[i] += emoticons[change] * (10 - sale) // 10
bt(new_s, change + 1)
print(answer)
class bt:
def __init__(self, users, emoticons):
self.answer = [0,0]
users.sort(key = lambda x : x[1])
self.table = [[] for _ in range(4)]
for user in users:
i = user[0]//10 if user[0]%10 != 0 else user[0]//10 - 1
self.table[i].append(user[1])
self.users = users
self.emoticons = emoticons
def bt_(self, s = [0, 0, 0, 0], change = 0):
if change == len(self.emoticons):
self.check(s)
return
for sale in range(1, 5):
new_s = s.copy()
for i in range(sale):
new_s[i] += self.emoticons[change] * (10 - sale) // 10
self.bt_(new_s, change + 1)
def check(self, s):
new_answer = [0, 0]
for i in range(4):
count = len(self.table[i])
for user_price in reversed(self.table[i]):
if s[i] >= user_price :
break
count -= 1
new_answer[0] += count
new_answer[1] += (len(self.table[i]) - count) * s[i]
self.answer = max(new_answer, self.answer)
def solution(users, emoticons):
back_tracking = bt(users, emoticons)
back_tracking.bt_()
return back_tracking.answer