TIL - 프로그래밍/Python 알고리즘

이것이 취업을 위한 코딩 테스트다 CHAPTER 13 DFS/BFS 문제

chaemj97 2023. 4. 9. 20:35
728x90
Q 15. 특정 거리의 도시 찾기
'''
    Chapter 13. 특정 거리의 도시 찾기

    접근법 1
        최단 거리 구하는 문제 -> bfs

        현 위치에서 갈 수 있는 도시를 다 방문
            최단 거리보다 더 적은 거리로 방문했으면 방문 이어서 하기
            최단 거리로 방문했으면 그 이상 방문은 최단거리보다 크기 때문에 그 도시에서 더 나아가지 않기

'''
from collections import deque
import sys
input = sys.stdin.readline

# 도시의 개수, 도로의 개수, 거리정보(최단거리),출발도시번호
N,M,K,X = map(int,input().split())
# 도로
load = [[] for _ in range(N)]
for _ in range(M):
    A,B = map(int,input().split())
    load[A-1].append(B-1)

# 방문 표시
visited = [0]*N
que = deque()
# 시작점
visited[X-1] = 1
que.append([X-1,0])

# 최단거리가 K인 도시들
answer = []

while que:
    # 현 위치, 이동 거리
    current, dist = que.popleft()
    # 이동
    # 현 위치에서 이동할 수 있는 도시
    for i in load[current]:
        # 방문한 적 없다면
        if visited[i] == 0:
            # 방문
            visited[i] = 1
            # 최단거리가 K인 도시인가?
            if dist+1 == K:
                answer.append(i+1)
            # 최단거리가 K보다 크면 더 방문할 필요X
            # 최단거리가 K보다 작을 때만 이동
            elif dist+1 < K:
                que.append([i,dist+1])

# 정답 출력
if answer:
    for a in sorted(answer):
        print(a)
else:
    print(-1)

 

Q 16. 연구소
'''
    Chapter 13. 연구소

    접근법 1
        목표 : 벽 3개 세운 후 안전 영역의 최대 크기 구하기

        지도의 크기 최대 8*8이므로 벽 3개를 세우는 모든 경우의 안전 영역 크기 구하기
            벽 3개 세우기 : 완전 탐색
            안전 영역 크기 세기 : DFS
'''
from itertools import combinations
from copy import deepcopy
import sys
input = sys.stdin.readline

# 지도의 세로, 가로
N,M = map(int,input().split())
# 지도 (0:빈칸, 1:벽, 2: 바이러스)
arr = [list(map(int,input().split())) for _ in range(N)]

# 빈칸의 위치
empty = []
# 바이러스 위치
virus = []
for r in range(N):
    for c in range(M):
        if arr[r][c] == 0:
            empty.append([r,c])
        elif arr[r][c] == 2:
            virus.append([r,c])

# 안전 영역 기본값
safe_default = len(empty)
# 안전 영역 최댓값
answer = 0

# 빈칸 3개 고르기
for c in combinations(empty,3):
    temp = deepcopy(arr)
    # 빈칸 3개를 벽으로 바꾸기
    for i,j in c:
        temp[i][j] = 1

    # 현재 경우 안전 영역 크기
    cnt = safe_default-3

    # 방문 체크
    visited = [[0]*M for _ in range(N)]
    
    # 바이러스 퍼트리기
    for vr,vc in virus:
        # 방문
        visited[vr][vc] = 1
        stack = [[vr,vc]]
        while stack:
            # 현 위치
            cr,cc = stack.pop()
            # 이동
            for dr,dc in [[0,1],[-1,0],[1,0],[0,-1]]:
                nr = cr+dr
                nc = cc+dc
                # 범위내, 방문한 적 없는 빈칸
                if 0 <= nr < N and 0 <= nc < M and visited[nr][nc] == 0 and temp[nr][nc] == 0:
                    cnt -= 1
                    visited[nr][nc] = 1
                    temp[nr][nc] = 2
                    stack.append([nr,nc])
    answer = max(cnt,answer)

print(answer)

과거 풀이

https://chaemi720.tistory.com/173

 

[백준] 14502. 연구소 - Python

https://www.acmicpc.net/problem/14502 14502번: 연구소 인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서

chaemi720.tistory.com

 

Q 17. 경쟁적 전염
 

https://chaemi720.tistory.com/277

 

[백준] 18405. 경쟁적 전염 - Python (BFS 사용 X)

https://www.acmicpc.net/problem/18405 18405번: 경쟁적 전염 첫째 줄에 자연수 N, K가 공백을 기준으로 구분되어 주어진다. (1 ≤ N ≤ 200, 1 ≤ K ≤ 1,000) 둘째 줄부터 N개의 줄에 걸쳐서 시험관의 정보가 주어

chaemi720.tistory.com

 

Q 18. 괄호 변환
'''
    Chapter 13. 괄호 변환

    접근법 1
        단계가 있다 -> 구현/시뮬레이션

        두 문자열로 분리
            -> 여는 괄호 수와 닫는 괄호 수가 같을 때 stop

        올바른 괄호인지
            여는 괄호와 닫는 괄호가 세트인지 확인해야함
            -> 닫는 괄호가 나왔을 때 이전에 여는 괄호가 있었는지만 확인하면 됨
            -> 이미 균형잡힌 괄호이기 때문에

            DFS를 사용할 수도 있음

'''
# 올바른 괄호 문자열인지 확인하는 함수
def correct_check(p):
    cor = 0
    for i in p:
        # 여는 괄호
        if i == '(':
            cor += 1
        # 닫는 괄호
        else:
            # 여는 괄호가 없는데 닫는 괄호가 나온 경우
            if cor == 0:
                return False
            cor -= 1
    # 균형잡힌 괄호 문자열이므로 남은 괄호 없음
    return True

'''
# 올바른 괄호 문자열 DFS 사용하는 경우
def correct_check(p):
    stack = []
    for i in p:
        # 여는 괄호
        if i == '(':
            stack.append(i)
        # 닫는 괄호
        else:
            # 여는 괄호가 있어야 함
            if stack:
                stack.pop()
            else:
                return False
    # 균형잡힌 괄호 문자열이므로 남은 괄호 없음
    return True                
'''
    
# 두 문자열로 분리
def two_split(p):
    l = r = 0
    for i in range(len(p)):
        if p[i] == '(':
            l += 1
        else:
            r += 1
        # 균형잡힌 괄호 문자열인가?
        if r == l:
            return p[:i+1],p[i+1:]

        
def solution(p):
    # 1. 빈 문자열인 경우
    if p == '':
        return p
    
    # 2. 문자열 2개로 분리
    u,v = two_split(p)
    # 3. u가 올바른 괄호이면 v에 대해 solution(v)
    if correct_check(u):
        # 3-1. u와 붙인 후 return
        return u + solution(v)
    
    # 4. u가 올바른 괄호가 아니면
    else:
        # 4-1~4-3
        new_v = '(' + solution(v) + ')'
        # 4-4. u의 첫번째 마지막 제거하고, 나머지 뒤집기
        new_u = ''
        for j in u[1:-1]:
            if j == '(':
                new_u += ')'
            else:
                new_u  += '('
        # 4-5
        return new_v + new_u

 

Q 19. 연산자 끼워 넣기
'''
    Chapter 13. 연산자 끼워넣기

    접근법 1
        4가지 연산 다 생각해야 함 -> 완전 탐색

        연산 끝까지 진행 후 다음 경우 구해야 함 -> DFS 재귀

        '나누기'의 경우 음수 생각
            ex) -12 나누기 5 는 -(-(-12)나누기5의 몫))로 계산해서 -2가 답
                -12//5 == 3
                -12/5 == -2.4
                int(-12/5) == -2
   
'''
import sys
input = sys.stdin.readline

# 수의 개수
N = int(input())
# 수
number = list(map(int, input().split()))
# 연산자 수 (덧셈,뺄셈,곱셈,나눗셈)
plus, minus, mul, div = map(int, input().split())

# 결과 최대
max_answer = float('-inf')
# 결과 최소
min_answer = float('inf')

# 완전 탐색
def cal(i,num,plus,minus,mul,div):
    global max_answer,min_answer
    # 연산 끝
    if i == N:
        max_answer = max(max_answer,num)
        min_answer = min(min_answer,num)
        return 
    
    # 더하기
    if plus:
        cal(i+1, num+number[i], plus-1, minus, mul, div)
    # 빼기
    if minus:
        cal(i+1, num-number[i], plus, minus-1, mul, div)
    # 곱하기
    if mul:
        cal(i+1, num*number[i], plus, minus, mul-1, div)
    # 나누기
    if div:
        cal(i+1, int(num/number[i]), plus, minus, mul, div-1)

cal(1,number[0],plus,minus,mul,div)

print(max_answer)
print(min_answer)

 

Q 20. 감시 피하기
'''
    Chapter 13. 감시 피하기

    접근법 1
        목표 : 3개의 장애물을 설치해서 감시를 피하자    
   
        복도의 최대 크기 36, 장애물 3개 설치하는 최대 경우의 수 -> 36C3
        -> 완전 탐색

        장애물 3개 설치 후 선생님마다 4방향을 다 확인해본다
            확인하던 중 장애물 or 다른 선생님을 만나면 그 방향으로 확인 멈추기
            학생을 만나면
                다음 장애물 3개 설치 경우로 넘기기
       
'''
from itertools import combinations
from copy import deepcopy
import sys
input = sys.stdin.readline

# 3개의 장애물을 설치해서 감시로부터 피해라

# 복도 크기
N = int(input())
# 복도
arr = [list(input().split()) for _ in range(N)]

# 빈칸 위치
empty = []
# 선생님 위치
teacher = []
for r in range(N):
    for c in range(N):
        if arr[r][c] == 'T':
            teacher.append([r,c])
        elif arr[r][c] == 'X':
            empty.append([r,c])

# 감시
def check(temp):
    for tr,tc in teacher:
        # 4방향 확인
        for dr,dc in [[1,0],[-1,0],[0,1],[0,-1]]:
            nr = tr+dr
            nc = tc+dc
            while 0<=nr<N and 0<=nc<N:
                # 감시방향에 장애물 or 다른 선생님이 있는 경우 그 쪽 안봐도 됨
                if temp[nr][nc] == 'O' or temp[nr][nc] == 'T':
                    break
                # 학생 발견?
                if temp[nr][nc] == 'S':
                    return False
                # 감시방향으로 다음 칸 보기
                nr += dr
                nc += dc
    return True


# 빈칸 3개 설치
for install in combinations(empty,3):
    temp = deepcopy(arr)
    for i,j in install:
        temp[i][j] = 'O'

    # 감시에서 피할 수 있는가
    if check(temp):
        print('YES')
        exit(0)

# 모든 경우 감시를 피할 수 없었다.
print('NO')

 

Q 21. 인구 이동
'''
    Chapter 13. 인구 이동

    접근법 1
        모든 나라의 인접한 나라의 인구수를 확인한 뒤 연합을 만들어 인구 이동 처리
        인구이동을 할 수 없을 때까지
        -> bfs

        인구이동이 있었는지(== 연합이 있었는지)를 flag 변수를 통해 확인

'''
from collections import deque
import sys
input = sys.stdin.readline

# 땅의 크기, L명이상 R명 이하
N,L,R = map(int,input().split())
# 인구수
population = [list(map(int,input().split())) for _ in range(N)]

# 연합 찾기
def bfs(r,c):
    que = deque()
    que.append([r,c])

    # (r,c)와 연결된 나라
    connect = [[r,c]]
    # 연합 전체 인구
    connect_sum = population[r][c]

    while que:
        # 현 위치
        cr,cc = que.popleft()
        # 4방향 확인
        for dr,dc in [[1,0],[-1,0],[0,1],[0,-1]]:
            nr = cr+dr
            nc = cc+dc
            # 범위 내, 확인한 적 없는 나라, 인구차이가 L명이상 R명이하
            if 0<=nr<N and 0<=nc<N and visited[nr][nc] == 0 and L <= abs(population[cr][cc]-population[nr][nc]) <= R:
                visited[nr][nc] = 1
                que.append([nr,nc])
                connect.append([nr,nc])
                connect_sum += population[nr][nc]

    # 연결된 나라 인구 이동
    for i,j in connect:
        population[i][j] = connect_sum//len(connect)
    
    # 연결된 나라 수
    return len(connect)

# 인구 이동 횟수
answer = 0
while True:
    # 인구이동이 있었는가
    flag = False

    # 확인했는지
    visited = [[0]*N for _ in range(N)]

    for r in range(N):
        for c in range(N):
            # 확인한 적 없는 나라
            if visited[r][c] == 0:
                visited[r][c] = 1
                # 연합이 있는가?
                if bfs(r,c) > 1:
                    flag = True
                
    # 인구이동 없다
    if not flag:
        break 
    # 하루 끝
    answer += 1

print(answer)

 

Q 22. 블록 이동하기

https://chaemi720.tistory.com/289

 

[프로그래머스] 블록 이동하기 - Python

https://school.programmers.co.kr/learn/courses/30/lessons/60063 프로그래머스 코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는

chaemi720.tistory.com

 

728x90
반응형