[Samsung] 2022-2 오전 2번 산타의 선물공장
[Samsung] 2022-2 오전 2번 산타의 선물공장
Question
Language: Python
Difficulty: Platinum 4
해당 문제는 doubly linked list을 직접적으로 구현해서 문제를 풀이해야 시간 복잡도 내 풀이가 가능하다. 각 노드에 대한 컨테이너 인덱스를 저장해서 특정 노드의 위치를 파악할 때 시간 효율성을 높이도록 한다.
공장 설립
doubly linked list을 초기화 하는 부분으로 노드를 생성하고, 각 노드들의 양끝을 서로 연결한다.
n,m=queries[0][1],queries[0][2]
box_indexes=queries[0][3:n+3]
weights=queries[0][n+3:]
conveyor_indexes=[i for i in range(m)]
#box index에 대한 무게 매핑
node_maps={}
unit=n//m
heads=[]
tails=[]
#노드 생성
for conveyor_index in range(m):
start,end=unit*conveyor_index,unit*conveyor_index+unit
for i in range(start,end):
node_maps[box_indexes[i]]=[conveyor_index,Node(box_indexes[i],weights[i])]
#노드 연결
for conveyor_index in range(m):
start,end=unit*conveyor_index,unit*conveyor_index+unit
heads.append(node_maps[box_indexes[start]][1])
for i in range(start+1,end-1):
node_maps[box_indexes[i]][1].connect(node_maps[box_indexes[i-1]][1],node_maps[box_indexes[i+1]][1])
tails.append(node_maps[box_indexes[end-1]][1])
물건 하차
각 컨테이너 별로 헤드의 무게를 확인해서, 주어진 무게 기준보다 낮은 헤드들은 빼내고, 그렇지 않은 경우 헤드를 뒤로 옮긴다.
컨테이너가 고장이거나, 빈 컨테이너 인 경우 탐색을 하지 않는다. 만약, 무게 기준에 부합하지 않지만 노드가 1개만 존재하는 경우 뒤로 옮기는 작업을 생략한다.
weight_sum=0
for conveyor_index in range(m):
#컨베이어 벨트가 고장인 경우
if conveyor_index != find_parent_compressed(conveyor_indexes,conveyor_index):
continue
head=heads[conveyor_index]
tail=tails[conveyor_index]
#빈 노드인 경우
if head == None:
continue
#주어진 무게 기준 보다 낮은 경우 해당 list에서 값을 빼낸다.
if head.weight <= option:
weight_sum+=head.weight
#헤드 제거
if head.right != None:
head.right.left=None
del node_maps[head.id]
heads[conveyor_index]=head.right
#맨 앞의 박스를 맨 뒤로 옮긴다. 이때, 박스가 1개 뿐인 경우 작업 수행 X
elif head!=tail:
tail.right=head
head.left=tail
tails[conveyor_index]=head
head.right.left=None
heads[conveyor_index]=head.right
head.right=None
물건 제거
특정 물건을 제거하고자 하는 경우, 해당 노드의 컨테이너 인덱스를 찾은 다음, 노드의 left, right을 서로 연결해주므로써 노드의 연결을 끊고 노드를 삭제한다.
이때 해당 노드가 head, tail 인 경우 head, tail을 새로 지정하는 작업을 수행한다.
#물건 제거
elif command==300:
#해당 박스가 없으면 -1 출력
if option not in node_maps.keys():
print(-1)
continue
conveyor_index,node=node_maps[option]
conveyor_index=find_parent_compressed(conveyor_indexes,conveyor_index)
if node.left != None:
node.left.right=node.right
if node.right !=None:
node.right.left=node.left
#헤드 인경우
if node.left ==None:
heads[conveyor_index]=node.right
#테일인 경우
if node.right==None:
tails[conveyor_index]=node.left
print(option)
del node_maps[option]
물건 찾기
특정 노드의 위치를 찾는 작업 에서는, 노드가 있는 경우 해당 노드를 기준으로 위에 있는 노드를 포함해서 모두 앞으로 당기는 작업을 수행해야한다.
만일 노드가 헤드인 경우에는 위의 작업을 수행할 필요가 없다.
#해당 박스가 없으면 -1 출력
if option not in node_maps.keys():
print(-1)
continue
conveyor_index,node=node_maps[option]
conveyor_index=find_parent_compressed(conveyor_indexes,conveyor_index)
#이미 헤드인 경우 작업 생략
if node != heads[conveyor_index]:
heads[conveyor_index].left=tails[conveyor_index]
tails[conveyor_index].right=heads[conveyor_index]
node.left.right=None
tails[conveyor_index]=node.left
node.left=None
heads[conveyor_index]=node
컨테이너 고장
컨테이너가 고장인 경우 인접한 컨테이너로 노드들을 옮기는 작업을 수행해야한다. 이때 기존 컨테이너에 있던 노드들의 컨테이너 인덱스를 변경해줘야하는데, 이를 일일히 수행하게 되면 시간 효율성이 따라진다. 따라서 컨테이어 인덱스에 대한 매핑을 저장해서 기존 컨테이너 인덱스를 새로운 컨테이너 인덱스로 연결해준다.이를 위해 multiple-disjoint set을 활용해서 연결한다.
def find_parent_compressed(parents,x):
if x!=parents[x]:
parents[x]=find_parent_compressed(parents,parents[x])
return parents[x]
def union_parents(parents,x,y):
pre_x=find_parent_compressed(parents,x)
pre_y=find_parent_compressed(parents,y)
parents[pre_x]=pre_y
current_converyor_index=option-1
#이미 고장 인경우
if current_converyor_index != find_parent_compressed(conveyor_indexes,current_converyor_index):
print(-1)
continue
moved_conveyor_index=0
#정상적인 컨테이너를 찾을 때까지 탐색 수행
for i in range(1,m):
moved_conveyor_index=(current_converyor_index + i)%m
#탐색한 다음 컨베이어가 고장인 경우 넘어간다.
if moved_conveyor_index == find_parent_compressed(conveyor_indexes,moved_conveyor_index):
break
#해당 컨테이너에 있던 노드의 컨테이너 인덱스를 변경한다.
union_parents(conveyor_indexes,current_converyor_index,moved_conveyor_index)
tails[moved_conveyor_index].right=heads[current_converyor_index]
heads[current_converyor_index].left=tails[moved_conveyor_index]
tails[moved_conveyor_index]=tails[current_converyor_index]
heads[current_converyor_index]=None
tails[current_converyor_index]=None
Solution
from collections import deque
from math import inf
from os.path import dirname,join
class Node:
def __init__(self,id,weight):
self.id=id
self.weight=weight
self.left=None
self.right=None
def __str__(self):
return "[id: %d, weight: %d]" % (self.id,self.weight)
def connect(self,left,right):
self.left=left
left.right=self
self.right=right
right.left=self
def find_parent_compressed(parents,x):
if x!=parents[x]:
parents[x]=find_parent_compressed(parents,parents[x])
return parents[x]
def union_parents(parents,x,y):
pre_x=find_parent_compressed(parents,x)
pre_y=find_parent_compressed(parents,y)
parents[pre_x]=pre_y
def solution():
n,m=queries[0][1],queries[0][2]
box_indexes=queries[0][3:n+3]
weights=queries[0][n+3:]
conveyor_indexes=[i for i in range(m)]
#box index에 대한 무게 매핑
node_maps={}
unit=n//m
heads=[]
tails=[]
#노드 생성
for conveyor_index in range(m):
start,end=unit*conveyor_index,unit*conveyor_index+unit
for i in range(start,end):
node_maps[box_indexes[i]]=[conveyor_index,Node(box_indexes[i],weights[i])]
#노드 연결
for conveyor_index in range(m):
start,end=unit*conveyor_index,unit*conveyor_index+unit
heads.append(node_maps[box_indexes[start]][1])
for i in range(start+1,end-1):
node_maps[box_indexes[i]][1].connect(node_maps[box_indexes[i-1]][1],node_maps[box_indexes[i+1]][1])
tails.append(node_maps[box_indexes[end-1]][1])
for command, option in queries[1:]:
#물건 하차
if command == 200:
weight_sum=0
for conveyor_index in range(m):
#컨베이어 벨트가 고장인 경우
if conveyor_index != find_parent_compressed(conveyor_indexes,conveyor_index):
continue
head=heads[conveyor_index]
tail=tails[conveyor_index]
#빈 노드인 경우
if head == None:
continue
#주어진 무게 기준 보다 낮은 경우 해당 list에서 값을 빼낸다.
if head.weight <= option:
weight_sum+=head.weight
#헤드 제거
if head.right != None:
head.right.left=None
del node_maps[head.id]
heads[conveyor_index]=head.right
#맨 앞의 박스를 맨 뒤로 옮긴다. 이때, 박스가 1개 뿐인 경우 작업 수행 X
elif head!=tail:
tail.right=head
head.left=tail
tails[conveyor_index]=head
head.right.left=None
heads[conveyor_index]=head.right
head.right=None
print(weight_sum)
#물건 제거
elif command==300:
#해당 박스가 없으면 -1 출력
if option not in node_maps.keys():
print(-1)
continue
conveyor_index,node=node_maps[option]
conveyor_index=find_parent_compressed(conveyor_indexes,conveyor_index)
if node.left != None:
node.left.right=node.right
if node.right !=None:
node.right.left=node.left
#헤드 인경우
if node.left ==None:
heads[conveyor_index]=node.right
#테일인 경우
if node.right==None:
tails[conveyor_index]=node.left
print(option)
del node_maps[option]
#물건 확인
elif command == 400:
#해당 박스가 없으면 -1 출력
if option not in node_maps.keys():
print(-1)
continue
conveyor_index,node=node_maps[option]
conveyor_index=find_parent_compressed(conveyor_indexes,conveyor_index)
#이미 헤드인 경우 작업 생략
if node != heads[conveyor_index]:
heads[conveyor_index].left=tails[conveyor_index]
tails[conveyor_index].right=heads[conveyor_index]
node.left.right=None
tails[conveyor_index]=node.left
node.left=None
heads[conveyor_index]=node
print(conveyor_index+1)
#벨트 고장
else:
current_converyor_index=option-1
#이미 고장 인경우
if current_converyor_index != find_parent_compressed(conveyor_indexes,current_converyor_index):
print(-1)
continue
moved_conveyor_index=0
#정상적인 컨테이너를 찾을 때까지 탐색 수행
for i in range(1,m):
moved_conveyor_index=(current_converyor_index + i)%m
#탐색한 다음 컨베이어가 고장인 경우 넘어간다.
if moved_conveyor_index == find_parent_compressed(conveyor_indexes,moved_conveyor_index):
break
#해당 컨테이너에 있던 노드의 컨테이너 인덱스를 변경한다.
union_parents(conveyor_indexes,current_converyor_index,moved_conveyor_index)
tails[moved_conveyor_index].right=heads[current_converyor_index]
heads[current_converyor_index].left=tails[moved_conveyor_index]
tails[moved_conveyor_index]=tails[current_converyor_index]
heads[current_converyor_index]=None
tails[current_converyor_index]=None
print(current_converyor_index+1)
if __name__ == "__main__":
q=int(input())
queries=[list(map(int,input().split())) for _ in range(q)]
solution()
Failed Solution
아래는 deque를 활용하여 작성해본 코드이다.
from collections import deque,defaultdict
from math import inf
def find_parent_compressed(parents,x):
if x!=parents[x]:
parents[x]=find_parent_compressed(parents,parents[x])
return parents[x]
def union_parents(parents,x,y):
pre_x=find_parent_compressed(parents,x)
pre_y=find_parent_compressed(parents,y)
parents[pre_x]=pre_y
def solution():
n,m=queries[0][1],queries[0][2]
box_indexes=queries[0][3:n+3]
weights=queries[0][n+3:]
conveyor_indexes=[i for i in range(m)]
#box index에 대한 무게 매핑
node_maps={}
unit=n//m
heads=[]
tails=[]
conveyors=[deque() for _ in range(m)]
#deque 생성
for conveyor_index in range(m):
start,end=unit*conveyor_index,unit*conveyor_index+unit
for i in range(start,end):
node_maps[box_indexes[i]]=(conveyor_index,weights[i])
conveyors[conveyor_index].append((box_indexes[i],weights[i]))
for command, option in queries[1:]:
#물건 하차
if command == 200:
weight_sum=0
for conveyor_index in range(m):
#컨베이어 벨트가 고장인 경우
if conveyor_index != find_parent_compressed(conveyor_indexes,conveyor_index):
continue
conveyor=conveyors[conveyor_index]
if len(conveyor) == 0:
continue
#주어진 무게 기준 보다 낮은 경우 해당 list에서 값을 빼낸다.
if conveyor[0][1] <= option:
weight_sum+=conveyor[0][1]
del node_maps[conveyor[0][0]]
conveyors[conveyor_index].popleft()
#맨 앞의 박스를 맨 뒤로 옮긴다. 이때, 박스가 1개 뿐인 경우 작업 수행 X
elif len(conveyor) > 1:
conveyors[conveyor_index].append(conveyors[conveyor_index].popleft())
print(weight_sum)
#물건 제거
elif command==300:
#해당 박스가 없으면 -1 출력
if option not in node_maps.keys():
print(-1)
continue
conveyor_index,weight=node_maps[option]
conveyor_index=find_parent_compressed(conveyor_indexes,conveyor_index)
conveyors[conveyor_index].remove((option,weight))
print(option)
del node_maps[option]
#물건 확인
elif command == 400:
#해당 박스가 없으면 -1 출력
if option not in node_maps.keys():
print(-1)
continue
conveyor_index,weight=node_maps[option]
conveyor_index=find_parent_compressed(conveyor_indexes,conveyor_index)
node_index=conveyors[conveyor_index].index((option,weight))
temp=deque()
length=len(conveyors[conveyor_index])
for index in range(node_index,length):
temp.append(conveyors[conveyor_index].pop())
for index in range(node_index,length):
conveyors[conveyor_index].appendleft(temp.popleft())
print(conveyor_index+1)
#벨트 고장
else:
current_converyor_index=option-1
#이미 고장 인경우
if current_converyor_index != find_parent_compressed(conveyor_indexes,current_converyor_index):
print(-1)
continue
moved_conveyor_index=0
#정상적인 컨테이너를 찾을 때까지 탐색 수행
for i in range(1,m):
moved_conveyor_index=(current_converyor_index + i)%m
#탐색한 다음 컨베이어가 고장인 경우 넘어간다.
if moved_conveyor_index == find_parent_compressed(conveyor_indexes,moved_conveyor_index):
break
#해당 컨테이너에 있던 노드의 컨테이너 인덱스를 변경한다.
union_parents(conveyor_indexes,current_converyor_index,moved_conveyor_index)
temp=deque()
length=len(conveyors[current_converyor_index])
for index in range(length):
temp.append(conveyors[current_converyor_index].pop())
for index in range(length):
conveyors[moved_conveyor_index].append(temp.pop())
print(current_converyor_index+1)
if __name__ == "__main__":
q=int(input())
queries=[list(map(int,input().split())) for _ in range(q)]
solution()
댓글남기기