import sys
from collections import defaultdict,deque
def input():
return sys.stdin.readline().rstrip()
N,M = map(int,input().split())
material_dict = {'-1':True}
# dict를 고정시키는게 문제
material_pay = [float('inf') for _ in range(10000)]
queue = deque()
for k in range(N):
name,pay = input().split()
material_dict[name] = k+1
material_pay[k+1] = int(pay)
queue.append(material_dict[name])
graph = defaultdict(list)
total_recipe_list = [input() for _ in range(M)]
recipe_cnt = defaultdict(int)
recipe_dict = defaultdict(list)
recipe_order = defaultdict(list)
total_recipe_list.sort()
for total_recipe in total_recipe_list:
complete_name,arg = total_recipe.split('=')
recipe = arg.split('+')
if material_dict.get(complete_name):
complete_idx = material_dict[complete_name]
else:
complete_idx = len(material_dict.keys())
material_dict[complete_name] = complete_idx
recipe_idx = (complete_idx,recipe_cnt[complete_idx])
recipe_cnt[complete_idx] += 1
temp_set = set()
temp = defaultdict(int)
for res in recipe:
cnt,name = int(res[0]),res[1:]
if material_dict.get(name):
name_idx = material_dict[name]
else:
name_idx = len(material_dict.keys())
material_dict[name] = name_idx
if recipe_idx not in graph[name_idx]:
graph[name_idx].append(recipe_idx)
temp_set.add(name_idx)
temp[name_idx] += cnt
recipe_order[complete_idx].append(temp_set)
recipe_dict[complete_idx].append(temp)
flag = True
total_len = len(material_dict) - 1
cnt = 0
while queue:
name_idx = queue.popleft()
if graph.get(name_idx):
for next_idx,recipe_idx in graph[name_idx]:
# 다시 들어왔을때 문제
if name_idx in recipe_order[next_idx][recipe_idx]:
recipe_order[next_idx][recipe_idx].remove(name_idx)
if not len(recipe_order[next_idx][recipe_idx]):
temp = 0
for mat,val in recipe_dict[next_idx][recipe_idx].items():
temp += material_pay[mat]*val
if material_pay[next_idx] > temp:
queue.append(next_idx)
material_pay[next_idx] = temp
if material_dict.get('LOVE'):
result = material_pay[material_dict['LOVE']]
if result == float('inf'):
print(-1)
elif result > 1000000000:
print(1000000001)
else:
print(result)
else:
print(-1)
이 문제는 여러번 틀렸던 문제였다.
풀면서 주의해야할 점은 다음과 같다.
처음에 주어지는 재료의 개수가 N의 최대가 50이라고 했지
모든 레시피에서 주어지는 재료의 개수가 50인게 아니다.
그래서 이걸 저장을 할때에는 넉넉하게해줘야한다.
두번째 여기서는 사이클같은 구조가 생길수 있다.
즉 위상정렬에서 한번 검사했던 노드이지만,
들어간 재료 중 하나가 최저값이 갱신이 되서, 현재 만들어지는 재료가 더 줄어둘수도 있다.
세번째 들어오는 입력에서 같은 재료가 여러번에 나뉘어서 들어 올수 있다.
네번째 같은 재료를 만드는 레시피가 여러종류가 있을 수 있다.
그래서 이 문제를 풀때 기본 컨셉은 다음과 같다.
각 완성재료를 만드는 전체 레시피들은 recipe_dict에 넣어줬다.
같은 완성재료를 만드는 방법이 여러개라면, recipe_cnt로 각 완성재료를 만드는 n번째 idx 이런식으로 구분을 해주었다.
그리고 위상정렬을 위해 order를 저장해주는 방식은
recipe_order[완성재료][레시피idx] 에 들어간 재료를 set으로 저장을 해주었다.
그리고 가장 중요한 그래프는 다음과 같이 저장을 해주었다.
graph라는 dictionary에 한 완성재료를 만드는 부품에 각각 (완성재료,완성재료를 만드는 레시피의 번호)를 저장시켜주었다.
그리고 material_dict은 재료명 대신 숫자로 관리하기 위해 만들어놨다.
material_pay는 각 재료를 만드는데 최소비용을 저장시켜놓은 리스트이다.
그러면 위상정렬을 어떻게 시작하면 되면
최초에 주어진 재료 N개를 queue에 넣어준다.
그리고 그 queue에서 하나씩 꺼내서
graph[name_idx]를 통해
이 재료로 만들 수 있는 레시피들에서 이 재료를 삭제시켜주었다.
이때 나중에 또 방문할 수 있으니, 있는지 확인을 하고 제거를 해준다.
그리고 이 recipe_order에 있는 set이 전부 사라져서 길이가 0 이면,
그때 queue에 들어갈수 있는지 확인해준다.
우리가 저장해놓은 recipe_dict을 이용해서 현재 재료들의 최저가로 만들었을때 나오는 비용을 계산한다.
비용을 계산한 후, material_pay에 있는 값과 비교를 해서
최저가가 갱신이 되면 queue에 넣어준다.
그리고 이 반복문은 queue가 빌때까지 무한 반복해준다.
그러면 우리는 결과를 출력할때 두가지로 먼저 나눈다.
레시피와 재료에 아예 LOVE가 없을때, 그때는 -1을 출력해준다.
레시피와 재료에 있지만, 초기값인 float('inf')일때에는 -1을 출력해준다.
왜냐하면 어디선가 재료가 부족해서 LOVE에 아예 접근을 못한상태이기 때문이다.
그리고 1000만이 넘었을때에는 1000만1 그 외에는 재료값을 출력하게 해준다.
푼 사람들 코드 중에 제가 푼 코드보다 가독성이 좋고, 좋은 코드들이 있다.
가독성을 늘리는 방법은 중간에 제가했던 사전작업과 위상정렬을 동시에 해버리면된다.
어떤 상황에서 큐에 들어가는지와 어떤상황에서 종료를 해야하는지 파악하면 풀 수 있는 문제이다.
즉 들어온 입력들 전체를 while문을 돌리면서
단 한번도 최저값이 갱신된적이 없거나, 새로운 재료가 생기지 않았다면, 종료를 해주면 된다.
'알고리즘 > 백준' 카테고리의 다른 글
[BOJ/백준] 1799 비숍 (0) | 2021.06.18 |
---|---|
[BOJ/백준] 2233 사과 나무 (0) | 2021.06.14 |
[BOJ/백준] 15653 구슬 탈출 4 (0) | 2021.06.14 |
[BOJ/백준] 21944 문제 추천 시스템 Version2 (0) | 2021.06.11 |
[BOJ/백준] 21943 연산 최대로 (0) | 2021.06.11 |