문제후기

  1. 문제를 보고 DP를 활용하는 문제라고 판단했습니다.
  2. 계산을 편하게 하기 위해서 좌표와 메트릭스의 크기를 동일하게 하고 계산했습니다.
  3. 물 웅덩이가 없는 경우 해당 위치까지 똑같은 크기만큼 이동했기 때문에 비교할 필요 없이 더하면 됩니다.
  4. 물 웅덩이가 있는 경우 값을 0으로 만들어 제외했습니다.

문제링크

 

코딩테스트 연습 - 등굣길

계속되는 폭우로 일부 지역이 물에 잠겼습니다. 물에 잠기지 않은 지역을 통해 학교를 가려고 합니다. 집에서 학교까지 가는 길은 m x n 크기의 격자모양으로 나타낼 수 있습니다. 아래 그림은 m =

programmers.co.kr


def solution(m, n, puddles):

    # 매트릭스 제작.
    matrix = [[0] * (m + 1) for _ in range(n + 1)]

    # 출발, 집 좌표
    matrix[1][1] = 1

    # DP 시작
    for y in range(1, n + 1):
        for x in range(1, m + 1):
            # 출발 값
            if y == 1 and x == 1:
                continue
            # 웅덩이가 있는 경우 패쓰
            if [x, y] in puddles:
                matrix[y][x] = 0
            else:
                # 해당 위치 값은 이전값 2개의 합.
                matrix[y][x] = matrix[y - 1][x] + matrix[y][x - 1]

    return matrix[n][m] % 1000000007

문제후기

  1. 문제에서 n이 1이상 100이하인 것과 경기 결과가 1개 이상 4,500개 이하인 것을 보고 완전탐색이 가능하다고 판단했습니다.
  2. 입출력 예시에서처럼 5번 선수는 다른 선수와 경기를 하지 않아도 2번 선수를 통해서 결과를 유추할 수 있다는 것을 확인할 수 있습니다.
  3. A선수가 B선수를 이기고 B 선수가 C선수를 이긴다면 A가 C를 이긴다는 조건이 성립합니다.( A>B이고 B > C이면 A>C)
  4. 유추를 통해 이길 수 있는 선수와 지는 선수를 set 집합을 통해 저장 후 결과를 추출했습니다.

문제링크

 

코딩테스트 연습 - 순위

5 [[4, 3], [4, 2], [3, 2], [1, 2], [2, 5]] 2

programmers.co.kr


def solution(n, results):
    # 선수 경기 결과 hash로 저장
    matrix = dict()

    # 이기는 사람 집합과 지는 사람 집합
    for i in range(1, n + 1):
        matrix[i] = [set(), set()]

    # 경기 결과 입력
    for i in results:
        matrix[i[0]][0].add(i[1])
        matrix[i[1]][1].add(i[0])

    # 만약 A > B 이고 B > C 인경우 A>C인 관계(>는 이긴다)
    # 모든 선수를 통해 경기하지 않아도 결과를 아는 경우 저장.
    for num, match in matrix.items():
        if match[0] and match[1]:
            for win in match[0]:
                matrix[win][1].update(match[1])
            for lose in match[1]:
                matrix[lose][0].update(match[0])

    # 만약 이기는 경우, 지는 경우, 자기 자신의 합이 모든 선수의 수인 경우 +1
    answer = 0
    for num, match in matrix.items():
        if len(match[0]) + len(match[1]) + 1 == n:
            answer += 1
    return answer
   

문제 후기

  1. (만일 가능한 경로가 2개 이상일 경우 알파벳 순서가 앞서는 경로를 return 합니다.) 해당 제한사항을 가능한 이라는 것을 인지하지 못하고 단순하게 알파벳이 먼저 오면 해결하려 해서 틀리게 됐습니다.
  2. DFS와 알파벳 순서 2가지를 생각해보며 다시 알고리즘을 짰고 스택을 활용하여 문제를 풀었습니다.
  3. 해당 알고리즘의 경우 도착 or 항공편이 없는 경우 먼저 answer에 들어가게 했으며 마지막에 reverse를 통해 결과값을 제출했습니다.

출처 : programmers.co.kr/learn/courses/30/lessons/43164?language=python3

 

코딩테스트 연습 - 여행경로

[[ICN, SFO], [ICN, ATL], [SFO, ATL], [ATL, ICN], [ATL,SFO]] [ICN, ATL, ICN, SFO, ATL, SFO]

programmers.co.kr

def solution(tickets):

    node_dict = {}      # 노드 hash화
    # 출발지를 key로 도착지를 value로 지정.
    for city in tickets:
        # dict.get(a,b) = dict에서 key가 a인 것을 찾고 없으면 b값을 넣겠다.
        node_dict[city[0]] = node_dict.get(city[0], []) + [city[1]]

    # 알파벳 역순으로 정렬
    for city in node_dict:
        node_dict[city] = sorted(node_dict[city], reverse=True)


    stack = ["ICN"]
    answer = []

    # 스택에 값이 있는동안 계속 반복
    while stack:
        now_city = stack[-1]
        # 만약 해당 도시 출발 항공표가 없을 때 answer에 추가.
        if now_city not in node_dict or node_dict[now_city] == []:
            answer.append(stack.pop())
        else:
            # 해당 도시 출발 항공표가 있을 때 다음 도시 pop해서 스택에 추가
            stack.append(node_dict[now_city].pop())
    # 역순이므로 다시 정렬
    answer.reverse()
    return answer

문제후기

  1. operation에 들어있는 문자열을 판별하여 최대값과 최솟값을 찾아서 제외하고 결과적으로 최대값과 최솟값을 찾는 문제로 해석했습니다.
  2. 파이썬에서 제공하는 heapq 모듈은 최소 힙을 기준으로 하므로 최대힙을 어떻게 처리할까 생각하다가 tuple로 최대힙을 만들어서 했습니다.
  3. tuple로 최대힙을 만든경우 추출된 값을 다른 힙에서도 제외해야해서 시간이 오래걸린다는 것을 알고 nlargest 함수를 활용했습니다.

출처 : programmers.co.kr/learn/courses/30/lessons/42628?language=python3

 

코딩테스트 연습 - 이중우선순위큐

 

programmers.co.kr

import heapq


def solution(operations):
    min_heap = []       # 힙 입력 배열
    for i in operations:
        # 숫자 입력인 경우
        if i[0] == "I":
            num = int(i.split()[1])
            # 숫자를 heap에 push
            heapq.heappush(min_heap, num)
        # 최댓값 추출인 경우
        elif i == "D 1":
            # 배열 안에 값이 없으면 패쓰
            if min_heap:
                # nlargest로 가장 최댓값 추출
                min_heap.remove(heapq.nlargest(1, min_heap)[0])
        # 최솟값 추출인 경우
        elif i == "D -1":
            # 배열 안에 값이 없으면 패쓰
            if min_heap:
                # pop으로 최솟값 추출
                heapq.heappop(min_heap)
    # 배열에 값이 있는 경우 최댓값, 최솟값 추출
    # 없는 경우 [0,0]추출
    if min_heap:
        return heapq.nlargest(1, min_heap) + [heapq.heappop(min_heap)]
    return [0, 0]

문제후기

1. 처음에는 다익스트라 알고리즘을 활용해서 해결하려 했으나 다익스트라로는 답이 나오지 않아 알고리즘 변경했습니다.

2. 찾아보다가 크루스칼이라는 알고리즘(최소비용으로 모든 노드를 연결시키는 알고리즘)이 적절한 알고리즘이라는 것을 찾았습니다.

3. union-find를 하는 방법으로 배열을 사용하는 방법을 생각해서 해결했습니다.

4. set으로도 해결가능할 것 같아 set으로도 해봤습니다.

문제 설명

n개의 섬 사이에 다리를 건설하는 비용(costs)이 주어질 때, 최소의 비용으로 모든 섬이 서로 통행 가능하도록 만들 때 필요한 최소 비용을 return 하도록 solution을 완성하세요.

다리를 여러 번 건너더라도, 도달할 수만 있으면 통행 가능하다고 봅니다. 예를 들어 A 섬과 B 섬 사이에 다리가 있고, B 섬과 C 섬 사이에 다리가 있으면 A 섬과 C 섬은 서로 통행 가능합니다.

제한사항

  • 섬의 개수 n은 1 이상 100 이하입니다.
  • costs의 길이는 ((n-1) * n) / 2이하입니다.
  • 임의의 i에 대해, costs[i][0] 와 costs[i] [1]에는 다리가 연결되는 두 섬의 번호가 들어있고, costs[i] [2]에는 이 두 섬을 연결하는 다리를 건설할 때 드는 비용입니다.
  • 같은 연결은 두 번 주어지지 않습니다. 또한 순서가 바뀌더라도 같은 연결로 봅니다. 즉 0과 1 사이를 연결하는 비용이 주어졌을 때, 1과 0의 비용이 주어지지 않습니다.
  • 모든 섬 사이의 다리 건설 비용이 주어지지 않습니다. 이 경우, 두 섬 사이의 건설이 불가능한 것으로 봅니다.
  • 연결할 수 없는 섬은 주어지지 않습니다.

입출력 예

n costs return
4 [[0,1,1],[0,2,2],[1,2,5],[1,3,1],[2,3,8]] 4

입출력 예 설명

costs를 그림으로 표현하면 다음과 같으며, 이때 초록색 경로로 연결하는 것이 가장 적은 비용으로 모두를 통행할 수 있도록 만드는 방법입니다.


def solution(n, costs):
    answer = 0
    # 간선의 크기로 정렬
    costs.sort(key=lambda x: x[2])
    # union-find를 set으로 설정
    routes = set([costs[0][0]])

    # 모든 노드를 연결한 경우 멈춤.
    while len(routes) != n:

        for i, cost in enumerate(costs):
            # 만약 시작노드와 마지막 노드가 연결되면 싸이클이 발생하는 경우
            if cost[0] in routes and cost[1] in routes:
                continue
            # 싸이클이 발생하지 않는 경우
            if cost[0] in routes or cost[1] in routes:
                # set 새로고침
                routes.update([cost[0], cost[1]])
                answer += cost[2]
                # 반영된 간선을 시각적으로 보기 편하게 하기위해 삽입(없어도 됨)
                costs[i] = [-1, -1, -1]
                break
    return answer
def solution(n, costs):
    answer = 0
    # 간선의 크기로 정렬
    costs.sort(key=lambda x: x[2])
    # union-find를 배열로 설정
    chk_node = [i for i in range(n)]
    count = 0 # 크루스칼을 만족하는 경우 간선의 갯수는 노드 -1
    for n1, n2, cost in costs:
        # 만약 시작노드와 마지막 노드가 연결되면 싸이클이 발생하는 경우
        if chk_node[n1] != chk_node[n2]:
            # 끝 노드 union-find 실행
            temp = chk_node[n2]
            for i in range(n):
                if chk_node[i] == temp:
                    chk_node[i] = chk_node[n1]
            
            answer += cost
            count += 1
        if count == n - 1:
            break
    return answer

문제 후기

1. 문제를 보고 바로 든 생각은 DP(Dynamic Programming)을 활용해야겠다는 것입니다.

2. 위의 줄의 같은 idx or idx-1 중에서 높은 수를 더해서 더해주면서 내려오고 맨 마지막 줄에서 가장 큰 수를 구하는 방식으로 해결했습니다.

문제 설명

위와 같은 삼각형의 꼭대기에서 바닥까지 이어지는 경로 중, 거쳐간 숫자의 합이 가장 큰 경우를 찾아보려고 합니다. 아래 칸으로 이동할 때는 대각선 방향으로 한 칸 오른쪽 또는 왼쪽으로만 이동 가능합니다. 예를 들어 3에서는 그 아래칸의 8 또는 1로만 이동이 가능합니다.

삼각형의 정보가 담긴 배열 triangle이 매개변수로 주어질 때, 거쳐간 숫자의 최댓값을 return 하도록 solution 함수를 완성하세요.

제한사항

  • 삼각형의 높이는 1 이상 500 이하입니다.
  • 삼각형을 이루고 있는 숫자는 0 이상 9,999 이하의 정수입니다.

입출력 예

triangle result
[[7], [3, 8], [8, 1, 0], [2, 7, 4, 4], [4, 5, 2, 6, 5]] 30

def solution(triangle):
    # DP로 2번째 줄부터 시작
    for row in range(1, len(triangle)):
        for col in range(len(triangle[row])):
            # 비교할 2가지 값.
            calculation1, calculation2 = triangle[row][col], triangle[row][col]
            # idx -1 위치에서 내려온 것.
            if 0 <= col - 1 < len(triangle[row - 1]):
                calculation1 += triangle[row - 1][col - 1]
            # idx 위치에서 내려온 것.
            if 0 <= col < len(triangle[row - 1]):
                calculation2 += triangle[row - 1][col]
            # 둘 중 큰 값 선택
            triangle[row][col] = max(calculation1, calculation2)
    # 마지막 줄에서 가장 큰 값 선택
    return max(triangle[-1])

문제후기

1. 처음 문제를 보고 단어가 순차적으로 찾아간다는 것과 최소거리를 구하는 것을 보고 word를 노드, 변환가능한 단어관계를 간선으로 생각했습니다.

2. begin을 포함한 words를 노드와 간선으로 표시한 이후 BFS를 이용해서 답을 구했습니다. DFS보다 BFS가 더 효율적이라 생각했습니다.

문제 설명

두 개의 단어 begin, target과 단어의 집합 words가 있습니다. 아래와 같은 규칙을 이용하여 begin에서 target으로 변환하는 가장 짧은 변환 과정을 찾으려고 합니다.

1. 한 번에 한 개의 알파벳만 바꿀 수 있습니다. 2. words에 있는 단어로만 변환할 수 있습니다.

예를 들어 begin이 hit, target가 cog, words가 [hot,dot,dog,lot,log,cog]라면 hit -> hot -> dot -> dog -> cog와 같이 4단계를 거쳐 변환할 수 있습니다.

두 개의 단어 begin, target과 단어의 집합 words가 매개변수로 주어질 때, 최소 몇 단계의 과정을 거쳐 begin을 target으로 변환할 수 있는지 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 각 단어는 알파벳 소문자로만 이루어져 있습니다.
  • 각 단어의 길이는 3 이상 10 이하이며 모든 단어의 길이는 같습니다.
  • words에는 3개 이상 50개 이하의 단어가 있으며 중복되는 단어는 없습니다.
  • begin과 target은 같지 않습니다.
  • 변환할 수 없는 경우에는 0를 return 합니다.

입출력 예

begin target words return
hit cog [hot, dot, dog, lot, log, cog] 4
hit cog [hot, dot, dog, lot, log] 0

입출력 예 설명

예제 #1
문제에 나온 예와 같습니다.

예제 #2
target인 cog는 words 안에 없기 때문에 변환할 수 없습니다.


from _collections import deque


def solution(begin, target, words):

    answer = 0      # 결과값
    length = len(begin)     # 단어 길이
    word_node = {}          # 변환 가능한 단어를 노드로 변환
    words.insert(0, begin)  # begin값 words에 추가

    # words 값들을 노드변환
    for idx in range(len(words)):
        word_node[idx] = []

    # 변환가능한 단어를 간선으로 변환
    for idx in range(len(words)):
        for idx2 in range(idx + 1, len(words)):
            chk_word = 0
            for c in range(length):
                if words[idx][c] == words[idx2][c]:
                    chk_word += 1
            # 만약 다른 글자가 1개인 경우 변환가능한 단어
            if chk_word == length - 1:
                word_node[idx].append(idx2)
                word_node[idx2].append(idx)

    wait_visit = []     # 다음 거리의 노드
    visit = deque([0])  # 큐에 넣어서 순차적으로 노드 방문
    visited = [0] * len(words)      # 노드 방문 여부 체크
    visited[0] = 1      # begin값부터 시작
    

    while True:
        # 큐에 값이 있을때까지
        while visit:
            # 방문 노드
            now_word = visit.pop()
            
            # word_node에서 간선으로 방문가능한지 확인
            for idx in word_node[now_word]:

                # 이미 방문한 노드인지 확인
                if visited[idx] == 1:
                    continue
                # 만약 해당 노드가 target값이면 종료
                if words[idx] == target:
                    return answer + 1
                # 다음 큐 리스트에 추가
                wait_visit.append(idx)
                # 방문체크
                visited[idx] = 1
        # 노드간의 거리 
        answer += 1
        
        # 만약 다음 방문할 노드가 없으면 종료
        if wait_visit:
            visit = deque(wait_visit)
            wait_visit = []
        else:
            return 0

문제 후기

1. 처음에는 단순하게 진입 지점과 진출 시점을 기준으로 카메라를 설치하려했습니다. 해당 방법으로 했더니 정답 자체가 아니어서 알고리즘이 틀린 것으로 알게 됐다.

2. 2번째로는 나가는 시점을 기준으로 카메라를 설치했다. 메모이제이션을 통해 미리 최대 길이(60,000)만큼 배열을 만들고 나가는 지점에 카메라를 기록하는 방식으로 했다. 정확성에서는 통과했지만 효율성에서 케이스 한 개가 안됐다.

3. 3번째로 새로운 카메라가 추가된 경우 미리 나머지 차량을 확인하는 방식을 선택했다. 해당 알고리즘으로 했을 때 효율성 또한 만족하는 것으로 나왔다.

문제 설명

고속도로를 이동하는 모든 차량이 고속도로를 이용하면서 단속용 카메라를 한 번은 만나도록 카메라를 설치하려고 합니다.

고속도로를 이동하는 차량의 경로 routes가 매개변수로 주어질 때, 모든 차량이 한 번은 단속용 카메라를 만나도록 하려면 최소 몇 대의 카메라를 설치해야 하는지를 return 하도록 solution 함수를 완성하세요.

제한사항

  • 차량의 대수는 1대 이상 10,000대 이하입니다.
  • routes에는 차량의 이동 경로가 포함되어 있으며 routes[i][0]에는 i번째 차량이 고속도로에 진입한 지점, routes[i][1]에는 i번째 차량이 고속도로에서 나간 지점이 적혀 있습니다.
  • 차량의 진입/진출 지점에 카메라가 설치되어 있어도 카메라를 만난것으로 간주합니다.
  • 차량의 진입 지점, 진출 지점은 -30,000 이상 30,000 이하입니다.

입출력 예

routes return
[[-20,15], [-14,-5], [-18,-13], [-5,-3]] 2

입출력 예 설명

-5 지점에 카메라를 설치하면 두 번째, 네 번째 차량이 카메라를 만납니다.

-15 지점에 카메라를 설치하면 첫 번째, 세 번째 차량이 카메라를 만납니다.


def solution(routes):
    
    chk_camera = [0] * len(routes)      # 총 확인해야하는 차량 수
    count = 0   # 결과값
    routes.sort(key=lambda x:x[1])      # 진출 지점으로 정렬

    for car in range(len(routes)):
        # 만약 해당 차량이 카메라 테스트에서 통과한 경우 패스
        if chk_camera[car]:
            continue
        # 해당 차량이 지나가는 카메라가 없으므로 새로 만듬.
        camera = routes[car][1]
        count +=1
        
        # 새로 만든 카메라가 이후 차량 중에서 지나가는 차량이 있는지 확인
        for num in range(car + 1, len(routes)):
            if routes[num][0] <= camera <= routes[num][1]:
                chk_camera[num] = True

    return count

+ Recent posts