Algorithms/Simulation

외벽 점검 with 파이썬

Louisus 2020. 8. 7. 18:06
728x90
# return 최소한의 친구들 투입
# 외벽 길이(1~200), 취약 지점 위치(길이:1~15, 원소:0~n-1), 한시간동안 이동가능 거리(길이:1~8, 원소:1~100)

# 1. 시작점 선정
# 2. 각 시작점 별 시계 반시계 방향으로 갔을 때 다 들리는 값 구하기

from collections import deque
from itertools import permutations

# 친구 투입 후 다음 친구 투입
def nxt_idx(queue, d, start_idx=0):
    # 시작점
    start_num = queue[start_idx]
    # i = 거리
    for i in range(1, d+1):
        try:
            # 이동 가능 거리 안에서 다음 취약점 포함되는 경우
            if queue[start_idx + 1] == start_num + i:
                start_idx = start_idx + 1
        # 예외처리 안하는 경우 런타임 에러뜸
        except:
            break

    # 시작점을 이동하고 난 후 체크된 취약점 이외부터 시작
    return start_idx+1

def solution(n, weak, dist):
    dist.sort(reverse=True)
    weak = deque(weak)

    # 투입 친구 수
    for i in range(1, len(dist)+1):
        if i == 1:
            # 모든 취약점 지점에서 진행
            for _ in range(len(weak)):
                # 가장 많이 움직이는 친구 1명
                d = dist[0]
                # 총 이동 가능거리가 넉넉한 경우
                if weak[-1] <= weak[0] + d:
                    return 1
                else:
                    # 약한 지점만큼 회전하므로 원래 상태로 돌아감
                    weak.rotate(-1)
                    # 왼쪽 방향 회전 - 마지막 수 = 마지막 수 + 원 길이
                    weak[-1] = weak[-1] + n

            weak = deque(map(lambda x: x % n, weak))
        # 친구 1명 보다 2명이상 투입하는 경우
        else:
            # 투입되는 친구 리스트 조합
            dist2 = list(permutations(dist[:i]))
            for selected in dist2:
                for _ in range(len(weak)):
                    # 시작점 인덱스
                    start_idx = 0
                    # 친구 선택
                    for d in selected:
                        # weak list, 친구 커버 가능 길이, 시작점 인자로 전달해서 새로운 시작점 구함
                        start_idx = nxt_idx(weak, d, start_idx)
                        # 시작점이 다 경로에 도달한 경우
                        if start_idx == len(weak):
                            return i
                    # 모든 경우에 대해 조건 만족하지 않는 경우 weak 회전
                    weak.rotate(-1)
                    weak[-1] = weak[-1] + n
                # 처음 상태 weak로 복구
                weak = deque(map(lambda x: x % n, weak))
    return -1
MAX = 8

# (처리가능 영역, 취약점 리스트)
def shift(N, arr):
    # item = 취약점 리스트
    # [1,5,6,10]
    # 1 item - arr[0] = 0
    # 5 5-1 = 4
    # 6 6-1 = 5
    # 10 10-1 = 9
    # 각 취약점 별로 초기값 부터의 거리 리스트 / 취약점이 초기값 보다 작은 경우 처리가능 영역만큼 더함
    return [item - arr[0] if item >= arr[0] else item - arr[0] + N for item in arr]

# 다음 친구 투입
def recursive(weak, dist, num):
    # 이미 이전 친구가 다 커버한 경우
    if len(weak) == 0:
        return num
    # 더 이상 일할 친구가 없는 경우
    if len(dist) == 0:
        return MAX

    val = MAX
                # 다음으로 일 할 친구 바꿔가면서 반복해서 해당 인원으로 커버 가능한지 체크    
        for i in range(len(dist)):
        j = 0
        while j < len(weak) and dist[i] + weak[0] >= weak[j]:
            j += 1
        # 친구 1명 투입해서 끝내지 못한 경우 다음 친구가 이어서 처리 -> 일 마무리 되면 val 값에 친구 수가 입력되서 리턴
        # i번째 친구 제외 후 다음 친구로 반복
        val = min(val, recursive(weak[j:], dist[:i] + dist[i+1:], num + 1))
    return val

def solution(n, weak, dist):
    answer = MAX
    # 최대 처리가능 영역
    max_dist = dist.pop()

    # 취약점 개수만큼 반복
    for i in range(len(weak)):
        # 인자: 총 영역, 취약점 리스트
        tmp = shift(n, weak)
        # 최고 이동 가능한 친구
        d = max_dist
        j = 0
        # 첫번째 친구 투입
        # 인덱스가 취약점 길이보다 작고 시작점부터 커버 가능 거리가 증가되는 인덱스 위치의 취약점 보다 큰경우 -> 커버 가능영역이라 인덱스 +
        while j < len(tmp) and d + tmp[0] >= tmp[j]:
            j += 1

        # 인자로 다음친구가 처리해야될 weak 부분, 이동가능 거리 리스트, 현재 친구 수 넘김
        answer = min(answer, recursive(tmp[j:], dist, 1))
        # weak 회전
        weak = weak[1:] + [weak[0]]

    # MAX 값이 그대로 유지된 경우는 해결 못한 경우임
    if answer == MAX:
        answer = -1

    return answer