import sys
import heapq
from collections import defaultdict
input = sys.stdin.readline
class Algorithm():
def __init__(self,num):
self.num = num
self.min_heap = []
self.max_heap = []
def insert(self,pb_num,diff):
heapq.heappush(self.min_heap,(diff,pb_num))
heapq.heappush(self.max_heap,(-diff,-pb_num))
def find_heap(self,flag):
result = []
if flag > 0:
if self.max_heap:
while (-self.max_heap[0][1] not in number_set) or number_algo[-self.max_heap[0][1]][0] != self.num or number_algo[-self.max_heap[0][1]][1] != -self.max_heap[0][0]:
heapq.heappop(self.max_heap)
if not self.max_heap:
break
if self.max_heap:
result = [-self.max_heap[0][0],-self.max_heap[0][1]]
else:
if self.min_heap:
while (self.min_heap[0][1] not in number_set) or number_algo[self.min_heap[0][1]][0] != self.num or number_algo[self.min_heap[0][1]][1] != self.min_heap[0][0]:
heapq.heappop(self.min_heap)
if not self.min_heap:
break
if self.min_heap:
result = self.min_heap[0]
return result
class Difficulty():
def __init__(self,num):
self.num = num
self.min_heap = []
self.max_heap = []
def insert(self,pb_num):
heapq.heappush(self.min_heap,pb_num)
heapq.heappush(self.max_heap,-pb_num)
def find_heap(self,x):
result = []
if x > 0:
if self.min_heap:
while self.min_heap[0] not in number_set or (number_algo[self.min_heap[0]][1]) != self.num:
heapq.heappop(self.min_heap)
if not self.min_heap:
break
if self.min_heap:
result = self.min_heap[0]
else:
if self.max_heap:
while -self.max_heap[0] not in number_set or (number_algo[-self.max_heap[0]][1]) != self.num:
heapq.heappop(self.max_heap)
if not self.max_heap:
break
if self.max_heap:
result = -self.max_heap[0]
return result
N = int(input())
algo_set = set()
diff_set = set()
algo_dict = {}
diff_dict = {}
number_algo = {}
number_set = set()
for _ in range(N):
number,dif,algo = map(int,input().split())
if algo not in algo_set:
algo_dict[algo] = Algorithm(algo)
algo_set.add(algo)
if dif not in diff_set:
diff_dict[dif] = Difficulty(dif)
diff_set.add(dif)
algo_dict[algo].insert(number,dif)
diff_dict[dif].insert(number)
number_algo[number] = [algo,dif]
number_set.add(number)
M = int(input())
for i in range(M):
command,*arg = input().split()
if command == 'recommend':
G,x = map(int,arg)
print(algo_dict[G].find_heap(x)[1])
elif command == 'recommend2':
x = int(arg[0])
diff_check = 0 if x == 1 else float('inf')
pb_num_check = -1
for algo_num in algo_dict:
ch = algo_dict[algo_num].find_heap(x)
if not ch: continue
if x == 1:
if ch[0] >diff_check:
diff_check = ch[0]
pb_num_check = ch[1]
elif ch[0] == diff_check:
if pb_num_check < ch[1]:
pb_num_check = ch[1]
else:
if ch[0] < diff_check:
diff_check = ch[0]
pb_num_check = ch[1]
elif ch[0] == diff_check:
if pb_num_check > ch[1]:
pb_num_check = ch[1]
print(pb_num_check)
elif command == 'recommend3':
flag,L_num = map(int,arg)
result = -1
if flag == -1:
L_num = L_num + flag
while 0<=L_num<=100:
if L_num in diff_set:
ch = diff_dict[L_num].find_heap(flag)
if not ch:
L_num = L_num + flag
continue
result = ch
print(ch)
break
L_num = L_num + flag
if result == -1:
print(-1)
elif command == 'solved':
pb_num = int(arg[0])
number_set.remove(pb_num)
del number_algo[pb_num]
else:
pb_num,diff_num,algo_num = map(int,arg)
if algo_num not in algo_set:
algo_dict[algo_num] = Algorithm(algo_num)
algo_set.add(algo_num)
if diff_num not in diff_set:
diff_dict[diff_num] = Difficulty(diff_num)
diff_set.add(diff_num)
algo_dict[algo_num].insert(pb_num,diff_num)
diff_dict[diff_num].insert(pb_num)
number_algo[pb_num] = [algo_num,diff_num]
number_set.add(pb_num)
이 문제는 추천 방식이 총 3개이다.
첫번째 추천은 알고리즘 G에서 가장 난이도와 높은것과 낮은것
두번째 추천은 모든 알고리즘에서 가장 난이도가 높은것과 낮은것
세번째 추천은 주어진 난이도에서 가장 가까운 난이도의 문제를 추천이다.
그래서 나는 크게 2가지의 클래스를 만들어주었다.
각 클래스는 각각 max_heap과 min_heap을 만들어주었는데.
이리 한 이유는 세번째 추천과 , 첫번째추천, 두번째 추천이 flag에 따라 동일한 조건일때 반환해야하는 문제의 조건이 달랐기 때문이다.
각각 max_heap과 min_heap, 그리고 알고리즘 클래스는 알고리즘 번호 난이도 클래스는 난이도 번호를 저장시켜주었다.
그러면 추천이 들어올때마다 while문을 도려서 현재, heap에서 사라져야할 문제들에 대해서, 제거를 해주고,
남은 heap일떄 출력을 해주는 방식으로 했다.
그리고 2번째와 3번째는 난이도와 알고리즘 분류는 최대 100이기 때문에 전체탐색을 해주었다.
좀 더 자세하게 설명하면 2번째 추천은 전체 탐색
3번째 추천은 탐색을 하면서, 0과 100을 넘어갔는데도 만족하는 조건을 못 찾으면 -1을 출력하게 해주었다.
min_heap과 max_heap 그리고 남아있는 문제들에 대한 상태관리를 해야하기 때문에 코드가 길고 복잡했다.
import sys
from collections import defaultdict
input = sys.stdin.readline
const_value = 100000
N = int(input())
re1_dict = defaultdict(dict)
re2_dict = defaultdict(dict)
re3_dict = defaultdict(dict)
problem_dict = dict()
for _ in range(N):
p_num,l_num,g_num = map(int,input().split())
calc_num = l_num*const_value + p_num-1
re1_dict[g_num][calc_num] = 1
re2_dict[calc_num] = 1
re3_dict[l_num][p_num] = 1
problem_dict[p_num] = [l_num,g_num]
M = int(input())
for _ in range(M):
command,*arg = input().split()
if command == 'recommend':
g_num,x = map(int,arg)
if x > 0:
calc_num = max( re1_dict[g_num].keys())
else:
calc_num = min(re1_dict[g_num].keys())
l_num = calc_num//const_value
p_num = calc_num%const_value + 1
print(p_num)
elif command == 'recommend2':
x = int(arg[0])
if x > 0:
calc_num = max(re2_dict.keys())
else:
calc_num = min(re2_dict.keys())
l_num = calc_num//const_value
p_num = calc_num%const_value + 1
print(p_num)
elif command == 'recommend3':
x,find_L_num = map(int,arg)
if x < 0:
find_L_num = find_L_num + x
result = -1
while 0<=find_L_num<=100:
if re3_dict.get(find_L_num):
if x>0:
result = min(re3_dict[find_L_num].keys())
else:
result = max(re3_dict[find_L_num].keys())
break
find_L_num = find_L_num + x
print(result)
elif command == 'solved':
p_num = int(arg[0])
l_num,g_num = problem_dict[p_num]
calc_num = l_num*100000 + p_num-1
del re3_dict[l_num][p_num]
del re2_dict[calc_num]
del re1_dict[g_num][calc_num]
else:
p_num,l_num,g_num = map(int,arg)
calc_num = l_num*100000 + p_num-1
re1_dict[g_num][calc_num] = 1
re2_dict[calc_num] = 1
re3_dict[l_num][p_num] = 1
problem_dict[p_num] = [l_num,g_num]
두번째 방식은 실행 시간을 포기하는 것이다.
실행시간을 포기하고, 딕셔너리를 통해 추천을 해주는 것이다.
그러면 어떻게 해야할것인가 궁금할 것이다.
3번쨰 추천은 문제번호만 상관하면되기때문에 문제번호를 그대로 넣어주면 된다.
하지만 첫번째와 두번째 추천은
문제 난이도가 동일할시 문제번호의 대소를 구분해주어야한다.
문제에서 주어진 문제번호의 범위는 1~100000이다. 그리고 난이도의 범위는 1~100이다.
그러면
난이도 * 100000 + (문제번호 -1)을 해주면 한 숫자로 이 문제의 정보를 저장할 수 있다고 생각했다.
난이도가 가장 큰 값이기 때문에, 먼저 앞의 난이도를 비교해주고 앞의 2~3자리가 동일하면 나머지 문제번호를
통해 대소관계를 알수 있을거라 생각했다.
그래서 첫번째, 두번째 추천에는 이 계산된 값을 키 값으로하여, 딕셔너리를 만들어주고,
추천이 들어올때마다 min,max를 통해 원하는 값을 추출해주었다.
추출하는 방법도 간단하게 100000으로 나눈 몫이 난이도이고, 나머지에 +1을 해준 것이 문제번호이다.
그리고 solved를 해줄때에도
위의 공식을 이용해서 문제번호와 난이도를 계산해서 그 키 값을 제거해주는 된다.
대신 시간차가 많이 나는 방식이니, 시간을 줄이고 싶으신분은 min_heap과 max_heap을
어차피 통과만 되면 된다는 분들은 후자의 방법을 사용해도 될것이다.
'알고리즘 > 백준' 카테고리의 다른 글
[BOJ/백준] 1050 물약 (0) | 2021.06.14 |
---|---|
[BOJ/백준] 15653 구슬 탈출 4 (0) | 2021.06.14 |
[BOJ/백준] 21943 연산 최대로 (0) | 2021.06.11 |
[BOJ/백준] 21942 부품 대여장 (0) | 2021.06.11 |
[BOJ/백준] 21941 문자열 제거 (0) | 2021.06.11 |