알고리즘

백준 DFS 모음

삶은겨란 2022. 6. 24. 19:40

DFS(Depth First Search, 깊이 우선 탐색)

시작 정점에서 한 방향으로 계속 탐색하다가 더 이상 갈 곳이 없으면 가장 마지막 분기 간선이 있는 정점으로 돌아와 다른 방향의 정점의 탐색을 반복해 모든 정점을 방문하는 순회 기법

스택을 사용해 구현한다.

 

파이썬으로 DFS를 구현할 때  주의

import sys
sys.setrecursionlimit(100000)

 

2583번 영역 구하기

2644번 촌수계산

16173번 점프왕 쩰리

1167번 트리의 지름

1937번 욕심쟁이 판다

9466번 텀 프로젝트


2583번 영역 구하기

import sys
sys.setrecursionlimit(100000)
input=sys.stdin.readline

m,n,k=map(int, input().split())
graph=[[0]*n for _ in range(m)]

# 입력받은 사각형 내부를 1로 표시
for _ in range(k):
    lx,ly,rx,ry=map(int, input().split())
    for i in range(m-ry,m-ly): 
        for j in range(lx,rx):
            graph[i][j]=1
            


dx=[-1,1,0,0]
dy=[0,0,-1,1]
def dfs(x,y):
    global count
    graph[x][y]=1 # 좌표를 1로 변경
    count+=1 # 내부 칸수 세기
    for i in range(4): # 상하좌우 탐색
        nx=x+dx[i]
        ny=y+dy[i]

        if 0<=nx<m and 0<=ny<n: # 범위 안에 든다
            if graph[nx][ny]==0: # 비어있다
                dfs(nx,ny) # 재귀
count=0 # 내부 넓이
area=[]
for i in range(m):
    for j in range(n):
        if graph[i][j]==0: # 비어있는 부분이면
            dfs(i,j) # 탐색을 한다
            area.append(count)
            count=0
print(len(area))
area.sort()
for i in area:
    print(i, end=' ')

 

 

2644번 촌수계산

import sys
sys.setrecursionlimit(100000)
input=sys.stdin.readline

n=int(input())
a,b=map(int, input().split()) # 구하고자 하는 두 사람의 번호 a->b 이동거리를 계산
m=int(input()) # 관계의 개수
graph=[[] for _ in range(n+1)]
for i in range(m):
    x,y=map(int, input().split())
    graph[x].append(y)
    graph[y].append(x)
visited=[0]*(n+1)

def dfs(v):
    for i in graph[v]: # 현재 노드에 연결된 노드들을 돌면서
        if visited[i]==0: # 연결된 노드들에 아직 방문을 안했으면
            visited[i]=visited[v]+1 # 촌수를 증가시키면서 저장
            dfs(i)   
dfs(a)
print(visited[b] if visited[b]>0 else -1)

 

 

16173번 점프왕 쩰리

import sys
sys.setrecursionlimit(100000)
input=sys.stdin.readline

n=int(input())
graph=[list(map(int,input().split())) for _ in range(n)]
visited=[[0]*n for _ in range(n)]


dx=[0,1] # 아래로 이동
dy=[1,0] # 오른쪽으로 이동
# 다음에 이동할 수 있는 칸을 저장해서 
def dfs(x,y):

    if graph[x][y]==-1 :# 마지막 지점에 도달했으면
        print("HaruHaru")
        exit(0)
    visited[x][y]=1
    c=graph[x][y] # 한번에 이동 가능한 거리

    for i in range(2):
        nx=x+dx[i]*c
        ny=y+dy[i]*c

        if 0<=nx<n and 0<=ny<n: # 맵의 범위를 벗어나지 않으면
            if visited[nx][ny]==0:
                visited[nx][ny]=1
                dfs(nx,ny)

    return False
    

if dfs(0,0)==False: # 출발 위치
    print("Hing")

 

 

1167번 트리의 지름

# 임의의 노드에서 가장 먼 노드를 구하고 이 노드를 a라고 한다
# a에서 가장 먼 노드를 구해서 b라고 한다.
# a와 b의 거리가 트리의 지름이다.
# => dfs를 두번사용

import sys
sys.setrecursionlimit(10**9)
input=sys.stdin.readline

def dfs(v,dis):
    for x,y in graph[v]: #그래프에서 연결된 노드와 가중치를 가져옴
        if visited[x]==-1: # 연결된 노드가 방문하지 않은 곳이면
            visited[x]=dis+y # 가중치+ 그전의 가중치
            dfs(x,dis+y)

            
v=int(input()) # 정점의 개수

graph=[[] for _ in range(v+1)]
for i in range(v):
    x = list(map(int, input().split()))
    for j in range(1, len(x)-2, 2):
        graph[x[0]].append([x[j], x[j + 1]])

visited=[-1]*(v+1)
visited[1]=0

dfs(1,0) # 임의의 노드 1번에서 가장 먼 노드를 구한다.

start=visited.index(max(visited))
visited=[-1]*(v+1) # 다시 dfs로 탐색해야하므로 초기화
visited[start]=0
dfs(start,0)

print(max(visited))

 

 

1937번 욕심쟁이 판다

# dfs와 dp를 같이 이용해야 한다.

import sys
sys.setrecursionlimit(10**6)
input=sys.stdin.readline

n=int(input())
graph=[list(map(int, input().split())) for i in range(n)]
dp = [[0] * n for _ in range(n)] # 현재 노드에서 dfs했을 때 최대 몇칸 이동인지 기록

dx=[0,0,-1,1]
dy=[-1,1,0,0]

def dfs(x,y):
    if dp[x][y]==0: # 방문한 적이 없으면
        dp[x][y]=1 # 1로 바꾼다
        
    else: # 방문한 적이 있으면 그대로 리턴
        return dp[x][y]
    
    for i in range(4):
        nx = x + dx[i]
        ny = y + dy[i]
        if 0 <= nx < n and 0 <= ny < n:
            if graph[x][y]<graph[nx][ny]: # 이동한 칸의 대나무가 더 많아야 한다
                dp[x][y]=max(dp[x][y], dfs(nx,ny)+1) # 현재 위치와 상하좌우 탐색을 했을 때 어떤 것이 더 최대인지
    return dp[x][y] # 현재 노드의 최대 이동 칸수를 리턴

result = 0
for i in range(n):
    for j in range(n):
        result = max(result, dfs(i, j))

print(result)

 

 

9466번 텀 프로젝트

from itertools import cycle
import sys
sys.setrecursionlimit(10**6)
input=sys.stdin.readline

def dfs(v):
    global cnt
    cycle.append(v) # 현재 사람을 싸이클에 추가
    visited[v]=1 # 방문체크
    next=graph[v] # v가 지목한 사람

    if visited[next]==1: # 지목당한 사람의 방문이 체크되어 있으면
        if next in cycle: # 이 사람이 싸이클에 포함되어 있으면
            cnt+=cycle[cycle.index(next):] # 싸이클이 시작된 사람부터 끝까지
        return
    else: # 지목당한 사람의 방문이 체크되어 있지 않으면
        dfs(next) # 그 사람이 지목한 사람으로 넘어간다



t= int(input()) # 테스트 케이스

for _ in range(t):
    n=int(input()) # 학생의 수
    graph=[0]+list(map(int, input().split()))
    visited=[0]*(n+1)
    cnt=[]

    for i in range(n+1):
        if visited[i]==0: # 방문한 적이 없으면
            cycle=[] # 싸이클 초기화
            dfs(i)
            
    print(n-len(cnt)+1) # 0 고려하기