56. 충돌 위험 찾기

image.png

image.png

image.png

from collections import Counter

def solution(points, routes):
    '''
    같은좌표에 로봇이 2대이상 모이는 개수 return하기
    points: 포인트의 좌표를 나타내는 정수배열 -> 좌표는 100*100안에 있음
    routes: [출발포인트,도착포인트] 정수배열
    최단경로가 여러가지인 경우 행에서의 이동을 항상 먼저함.
    
    장애물이 있는게 아니니깐 그냥 시작좌표, 도착좌표 불러온다음에 r좌표 먼저 맞춰주고 c좌표 맞춰주면 됨
        굳이 BFS쓸필요 없음
    
    1. points를 각 index+1을 key로 갖도록 dic로 만들기
    2. routes를 하나씩 받아와서 dict[r[0]]부터 dict[[r[1]]]까지의 루트 인덱스를 리스트에 넣기
    3. 모든 루트 인덱스에서 같은 시간에 같은 인덱스에 존재하는 개수 count하기
    '''
    time = 0
    # points를 각 index+1을 key로 갖도록 dic로 만들기
    dic = {i + 1: tuple(point) for i, point in enumerate(points)}

    # 각 로봇의 전체 이동 경로
    all_paths = []
    
    for route in routes:
        path = []
        
        # 시작지점 dic에서 좌표 가져오기
        start = dic[route[0]]
        path.append(start)

        # 도착지점 좌표 dic에서 가져오기
        for i in range(len(route) - 1):
            end = dic[route[i+1]]
            
            # r 좌표 이동 먼저
            #도착지점의 r좌표가 시작지점의r좌표보다 크면 delta가 1, 작으면 delta가 -1
            delta = 1 if end[0] > start[0] else -1
            #시작지점이랑 도착지점의 r좌표가 같아질때까지 이동
            while start[0] != end[0]:
                start = (start[0] + delta, start[1])
                path.append(start)

            # r좌표 먼저 이동시킨 다음에 c좌표 이동
            # 도착지점의 c좌표가 시작지점의 c좌표보다 크면 delta가1, 작으면 delta가 -1
            delta = 1 if end[1] > start[1] else -1
            # start랑 end의 c좌표가 같아질때까지 이동
            while start[1] != end[1]:
                start = (start[0], start[1] + delta)
                path.append(start)
        
        all_paths.append(path)
        
        #각 path중 가장 긴 길이를 time으로 저장해야됨
        if len(path)>time:
            time = len(path)
            
    # 모든 경로에서 같은 시간에 같은 좌표에 존재하는 로봇의 개수세기
    answer = 0
    for t in range(time):
        intersect = []
        for p in all_paths:
            # 각 경로의 길이(이동하는시간)은 모두 다르니깐 예외처리
            if t < len(p):  
                intersect.append(p[t])
        
        # 해당시간 t에 COunter로 동일한 위치의 로봇 개수 세기
        count = Counter(intersect)
        
        # count에서 val이 2이상이면 answer에다 1씩 추가하기 
        for c in count.keys():
            if count[c] >= 2:
                answer += 1
                
    return answer

배울점

  1. 일단 dict의 key는 immutable type이 들어가야만함
    1. 따라서 list는 key로 쓸 수 없음
    2. 그건 알고있었는데 Counter 객체 쓸때 좌표값을 리스트로 저장하면 리스트가 key로 되니깐 에러발생함 → 좌표값 저장할때는 튜플로 저장하기 → 그래야 Counter 객체 쓸때 에러발생 안함
  2. 이문제는 장애물같은게 없으니깐 그냥 시작좌표 도착좌표 받은다음에 행먼저 이동시킨 후 열이동시키면 됐음
  3. 모든 로봇의 모든 경로를 저장해서 각 t마다 매번 intersect리스트에 counter객체를 생성하는데, 시간초과가 안뜨는게 신기함 → Counter는 $O(n)$으로 찾아서 가능했음
from collections import Counter
def solution(points, routes):
    answer = 0
    t = 0
    cur_points = []     # 각 로봇들이 현재 위치 저장
    targets = []    # 각 로봇들의 현재 목표 지점 저장
    target_idx = [1] * len(routes)  # 각 로봇들의 현재 목표 idx 저장
    x = len(routes)
    
    
    # 각 로봇의 현재 위치와 다음 목표 초기화
    for i in range(x):
        cur_points.append(points[routes[i][0] - 1])
        targets.append(points[routes[i][1] - 1])
        
    while any(idx != 0 for idx in target_idx):
        for i in range(x):  # 로봇 하나씩 선택
            if target_idx[i] != 0 and cur_points[i] != targets[i]:
                if cur_points[i][0] != targets[i][0]:
                    if cur_points[i][0] < targets[i][0]:
                        cur_points[i][0] += 1
                    else:
                        cur_points[i][0] -= 1
                elif cur_points[i][1] != targets[i][1]:
                    if cur_points[i][1] < targets[i][1]:
                        cur_points[i][1] += 1
                    else:
                        cur_points[i][1] -= 1
            # 목표 지점에 도달한 경우
            if cur_points[i] == targets[i]:
                if target_idx[i] != len(routes[0]) - 1:     
                    target_idx[i] += 1
                    targets[i] = points[routes[i][target_idx[i]] - 1]
                else:       # 마지막 목표 지점에 도달한 경우
                    target_idx[i] = 0
        
        counter = Counter(map(tuple, cur_points))
        # 중복된 좌표 총 개수...?
        t += 1            
     
    return answer
from collections import deque, defaultdict

def find_route(cur_point, tar_point):
    dx = tar_point[1] - cur_point[1]
    dy = tar_point[0] - cur_point[0]
    
    if dy != 0:
        dy = 1 if dy > 0 else -1
    
    if dx != 0:
        dx = 1 if dx > 0 else -1
    
    return dx, dy

def compare(cur_point, tar_point):
    if cur_point[0] == tar_point[0] and cur_point[1] == tar_point[1]:
        return True
    return False

def solution(points, routes):
    answer = 0
    
    # 자동 운송 시스템
    # (r, c)와 같이 2차원 좌표 n개
    # 운송 경로 m개의 포인트, 로봇은 할당된 포인트를 순서대로 방문
    # 로봇은 x대, 1초마다 r좌표와 c좌표중 1개만큼 감소됨.
    # 최단 경로로 이동하는데, 여러 가지일 경우, r좌표가 변하는 이동을 우선시함.
    # 이동 중 같은 좌표에 2대 이상이 모인다면 충돌할 가능성 존재. 
    # 현재 설정대로 로봇이 움직인다면? 몇번이나 일어나는가?
    
    
    # while문 내에서 robut의 수가 존재하는 동안,
    # 1초마다 각 로봇을 움직이고, overlapped point가 있다면 answer += 1
    
    
    # 각 로봇의 (현재 좌표, 목표 좌표는 순서만..)
    # 이때 목표 좌표의 len이 0이면 해당 로봇은 안움직임.
    robot_routes = deque([])
    
    for route in routes:
        start = route[0]
        end = route[1:]
        
        robot_routes.append((tuple(points[start-1]), end))
    
    robot_num = len(routes)
    robots = [False for _ in range(robot_num)]
    
    # 0초일때도 계산..
    tmp = defaultdict(int)
    for cur, _ in robot_routes:
        tmp[cur] += 1
        
    for v in tmp.values():
        if v > 1:
            answer += 1
     
    while sum(robots) != robot_num:
        # 겹치는 좌표의 수 계산을 위한 dict
        overlapped_point = defaultdict(int)
        
        # 1 step
        for i, route in enumerate(robot_routes):
            if robots[i]:
                continue

            cur_point = route[0]
            tar = route[1]
            if len(tar) == 0:
                robots[i] = True
                continue
            
            tar_point = points[tar[0] -1]
            dx, dy = find_route(cur_point, tar_point)
            
            # y축부터 이동.
            if dy != 0:    
                cur_point = (cur_point[0] + dy, cur_point[1])
            elif dx != 0:
                cur_point = (cur_point[0], cur_point[1] + dx)
            
            # 현재 목표 tar_point과 동일하다면? 
            # i) len(tar) == 1 이라면 현재 tar과 동일. -> 그 로봇은 끝남.
            is_same = compare(cur_point, tar_point)
            if is_same and len(tar) == 1:
                robots[i] = True
                tar = []
            elif (is_same and len(tar) > 1):
                tar = tar[1:]
            
            overlapped_point[cur_point] += 1
            
            robot_routes[i] = (cur_point, tar)
        
        for v in overlapped_point.values():
            if v > 1:
                answer += 1
        
        # print(f"{robots}")
        # print(f"{robot_routes}")
        # print(overlapped_point)
        # answer += len(overlapped_point)
        # print(f"answer : {answer}")  
    return answer
from collections import deque, defaultdict

class Robot:
    def __init__(self, id, points, routes):
        self.id = id
        self.points = points
        self.routes = deque(routes)
        self.pos = points[self.routes.popleft() - 1]
        self.target = points[self.routes[0] - 1]
        
    def update(self):
        if self.target is None:
            self.pos = [-self.id, -self.id] 
            return

        tx, ty = self.target
        x, y = self.pos
        
        dx = tx - x
        dy = ty - y

        if dx == 0 and dy == 0:
            self.routes.popleft()
            self.target = self.points[self.routes[0] - 1] if self.routes else None
            return

        if dx != 0:
            x += 1 if dx > 0 else - 1
        elif dy != 0:
            y += 1 if dy > 0 else -1

        self.pos = [x, y]
        
        if x == tx and y == ty:
            self.routes.popleft()
            self.target = self.points[self.routes[0] - 1] if self.routes else None

    def getPos(self):
        return self.pos

    def isArrived(self):
        return self.target is None

def solution(points, routes):
    answer = 0

    robots = [Robot(i + 1, points, route) for i, route in enumerate(routes)]
    
    while len(robots) > 1:
        counts = defaultdict(int)
        
        for robot in robots:
            counts[tuple(robot.getPos())] += 1
        
        answer += sum(1 for count in counts.values() if count > 1)

        robots = [robot for robot in robots if not robot.isArrived()]
        
        for robot in robots:
            robot.update()

    return answer

57. 택배배달과 수거하기

image.png

image.png

image.png

image.png

image.png

def solution(cap, n, deliveries, pickups):
    answer = 0
    '''
    cap: 트럭에 담을 수 있는 상자 수
    트럭으로 배달과 수거를 하는 최소 이동거리
    
    가장 멀리있는 집부터, 배달, 수거 -> 그리디
    
    1. 가장 큰 인덱스부터 검사하면서 cap만큼 빼기 -> 해당 인덱스의 deliveries와 pickups가 0이하가 될때까지
        위에서 루프를 돈 횟수 * 2 * (i+1) 만큼 이동한거임
    '''
    #length_d = 0
    #length_p = 0
    
    while deliveries or pickups:
    # while deliveries.count(0)==len(deliveries) and pickups.count(0)==len(pickups):
        # for i in range(len(deliveries)-1,-1,-1):
        #     if deliveries[i] != 0:
        #         length_d = i+1
        #         break
        # for j in range(len(pickups)-1,-1,-1):
        #     if pickups[j] != 0:
        #         length_p = j+1
        #         break
        
        #마지막에서 0인부분은 다 pop해버리기
        while deliveries and deliveries[-1]==0:
            deliveries.pop()
        while pickups and pickups[-1] == 0:
            pickups.pop()
        
        # 해당루프에서 이동거리 -> p와 d중 더 먼거리를 가야됨
        answer += max(len(deliveries),len(pickups))*2
        
        #한번 왔다갔다할때는 cap개수만큼 배달가능, cap개수만큼 수거가능
        #따라서 while문돌면서 d_cap = cap, p_cap = cap만큼 배달, 수거하기
       
        d_cap = cap
        it = len(deliveries)-1
        while d_cap != 0 and 0<=it<len(deliveries):
            if deliveries[it] <= d_cap:
                d_cap -= deliveries[it]
                deliveries[it] = 0
            else:
                deliveries[it] -= d_cap
                d_cap = 0
            it -= 1
            
        p_cap = cap
        it = len(pickups) -1
        while p_cap != 0 and 0<=it<len(pickups):
            if pickups[it] <= p_cap:
                p_cap -= pickups[it]
                pickups[it] = 0
            else:
                pickups[it] -= p_cap
                p_cap = 0
            it -= 1
        
    return answer