각 문제 유형별 풀 방법 정리1) 그래프의 모든 정점을 방문해야하는 문제DFS, BFS 뭐든 상관없다. 편한걸 쓰면 된다.2) 경로의 특징을 저장하는 문제예시로 각 정점에 숫자가 있고 a부터 b까지 경로를 구하는데 경로에 같은 숫자가 있으면 안된다는 문제. 각각의 경로마다 그 특징을 저장해두어야하는 문제는 DFS를 사용한다.3) 최단거리를 구하는 문제미로찾기등 최단거리를 구하는 경우 BFS가 좋다. DFS로 경로를 탐색하면 처음 발견하는 해답이 최단거리가 아닐수도 있지만 BFS는 현재노드에서 가까운 곳부터 찾기 때문에 경로를 탐색시 먼저 찾아지는 해답이 곧 최단거리다.4) 검색대상그래프가 많이 클때DFS가 좋다고한다.5) 검색대상의 규모가 별로 크지않고 검색시작지점으로부터 원하는 대상이 별로 멀지 않으면 BFS
|
01. BFS DFS의 기본틀 코드
예제 문제
https://www.acmicpc.net/problem/1260
1260번: DFS와 BFS
첫째 줄에 정점의 개수 N(1 ≤ N ≤ 1,000), 간선의 개수 M(1 ≤ M ≤ 10,000), 탐색을 시작할 정점의 번호 V가 주어진다. 다음 M개의 줄에는 간선이 연결하는 두 정점의 번호가 주어진다. 어떤 두 정점 사
www.acmicpc.net
https://velog.io/@hamfan524/%EB%B0%B1%EC%A4%80-1260%EB%B2%88-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC
[백준] 1260번-(Python 파이썬) - Bfs, Dfs
문제링크 : https://www.acmicpc.net/problem/1260
velog.io
위에 블로그를 보고 이론 감 익히기
# BOJ_1260
from collections import deque
import sys
read = sys.stdin.readline
# dfs 구현
def bfs(v):
q = deque()
q.append(v)
visit_list[v] = 1
while q:
v = q.popleft()
print(v, end=" ")
for i in range(1, n + 1):
if visit_list[i] == 0 and graph[v][i] == 1:
q.append(i)
visit_list[i] = 1
# DFS 구현
def dfs(v):
visit_list2[v] = 1
print(v, end=" ")
for i in range(1, n + 1):
if visit_list2[i] == 0 and graph[v][i] == 1:
dfs(i)
# 메인 코드
# 정점의 갯수 n, 간선의 갯수 m, 시작점 v
n, m, v = map(int, read().split())
graph = [[0] * (n + 1) for _ in range(n + 1)]
visit_list = [0] * (n + 1)
visit_list2 = [0] * (n + 1)
for _ in range(m):
a, b = map(int, read().split())
graph[a][b] = graph[b][a] = 1
dfs(v)
print()
bfs(v)
https://www.acmicpc.net/problem/2178
2178번: 미로 탐색
첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.
www.acmicpc.net
02. BFS 응용 - 최단거리 문제
예제문제
https://www.acmicpc.net/problem/2178
2178번: 미로 탐색
첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.
www.acmicpc.net
DFS로 뻘짓한 코드
# DFS로 푼다.
def dfs(cur_x, cur_y, visited, move_count):
visited[cur_x][cur_y] = True
for i in range(4):
next_x = cur_x + dx[i]
next_y = cur_y + dy[i]
if next_x <= -1 or next_x >=n or next_y <= -1 or next_y >= m:
continue
if not visited[next_x][next_y] and graph[next_x][next_y] == 1:
if next_x == n - 1 and next_y == m - 1:
return move_count
move_count = dfs(next_x, next_y, visited, move_count + 1)
min_move[next_x][next_y] = move_count
return move_count
# 메인문
move_count = 0
answer = dfs(0, 0, visited, move_count)
print(min_move)
print(answer)
BFS로 푼 코드
# BOJ_2178
from collections import deque
# 최단거리 문제를 풀 때는 BFS로 푼다.
def bfs(graph):
# (0,0) 에서 출발
q = deque([(0, 0)])
# 각 점에서의 최단거리를 나타낸다.
visited = [[0] * m for _ in range(n)]
dx = [0, 0, -1, 1]
dy = [-1, 1, 0, 0]
visited[0][0] = 1
graph[0][0] = 0
while q:
# print(q)
x, y = q.popleft()
for i in range(4):
nx = x + dx[i]
ny = y + dy[i]
if 0 <= nx < len(graph) and 0 <= ny < len(graph[0]):
if graph[nx][ny] == 1 and not visited[nx][ny]:
visited[nx][ny] = visited[x][y] + 1
q.append((nx, ny))
answer = -1
# 도착점에 이뤘을 때 0이 아니므로
if visited[-1][-1]:
# print(visited)
return visited[-1][-1]
return answer
# 입력
n, m = map(int, input().split())
graph = [list(map(int, input())) for _ in range(n)]
print(bfs(graph))
03. DFS 응용 - 연결 덩어리 문제
1) 그래프의 모든 정점을 방문해야하는 문제DFS, BFS 뭐든 상관없다. 편한걸 쓰면 된다. |
예제문제
https://www.acmicpc.net/problem/2667
2667번: 단지번호붙이기
<그림 1>과 같이 정사각형 모양의 지도가 있다. 1은 집이 있는 곳을, 0은 집이 없는 곳을 나타낸다. 철수는 이 지도를 가지고 연결된 집의 모임인 단지를 정의하고, 단지에 번호를 붙이려 한다. 여
www.acmicpc.net
DFS 코드
# BOJ_2667
# dfs 구현
def dfs(x, y):
global count
if 0 > x or x >= len(graph) or 0 > y or y >= len(graph[0]):
return False
# 각 이동별로 dfs
if graph[x][y] == 1:
count += 1
graph[x][y] = 0
for i in range(4):
nx = x + dx[i]
ny = y + dy[i]
# house_count[-1] += 1
# 그래프 내부의 점이면
dfs(nx, ny)
return True # 상하좌우 다 뻗으면 단지수를 하나 추가한다.
return False
# 메인
n = int(input())
graph = [list(map(int, input())) for _ in range(n)]
# 각 단지내 집의 수를 저장한 리스트
house_count = []
# 이동 좌표 - 상하좌우
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
count = 0
# DFS 돌리기 - 모든 정점을 방문해야 한다.
for i in range(n):
for j in range(n):
if dfs(i, j):
house_count.append(count)
# print(house_count)
count = 0
# 총 단지수와 각 단지내 집의 수 오름차순 출력
print(len(house_count))
house_count.sort()
for house in house_count:
print(house)
BFS로 푼 코드
from collections import deque
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]
def bfs(graph, a, b):
n = len(graph)
queue = deque()
queue.append((a, b))
graph[a][b] = 0
count = 1
while queue:
x, y = queue.popleft()
for i in range(4):
nx = x + dx[i]
ny = y + dy[i]
if nx < 0 or nx >= n or ny < 0 or ny >= n:
continue
if graph[nx][ny] == 1:
graph[nx][ny] = 0
queue.append((nx, ny))
count += 1
return count
n = int(input())
graph = []
for i in range(n):
graph.append(list(map(int, input())))
cnt = []
for i in range(n):
for j in range(n):
if graph[i][j] == 1:
cnt.append(bfs(graph, i, j))
cnt.sort()
print(len(cnt))
for i in range(len(cnt)):
print(cnt[i])
참고 블로그: https://hongcoding.tistory.com/71
예제:
https://www.acmicpc.net/problem/10026
10026번: 적록색약
적록색약은 빨간색과 초록색의 차이를 거의 느끼지 못한다. 따라서, 적록색약인 사람이 보는 그림은 아닌 사람이 보는 그림과는 좀 다를 수 있다. 크기가 N×N인 그리드의 각 칸에 R(빨강), G(초록)
www.acmicpc.net
DFS로 구현하게 되면 시간초과가 나오게된다.아래 처럼 sys.setrecursionlimit(10**6)을 따로설정해주면pypy3는 메모리 초과로 안되고python3에서 정답 코드로 매겨지게 된다.
# BOJ_10026
# 빨 초 파 각각 덩어리로 보는 갯수와
# 빨 = 초, 파를 각각 덩어리로 보는 갯수를 구하고 싶다.
# 1. 빨 초 파 각각 덩어리로 보는 갯수
# 색을 저장하고 같은 색이 모두 나올 때까지 돌리기
# 파란색 덩어리수는 같고, 빨 = 초만 다르게 구현해주면 된다.
# 애초에 색약 있을 때는 그래프에서 G부분을 R로 모두 바꿔준다.
import sys
sys.setrecursionlimit(10**6)
# dfs
def dfs(x, y, graph):
global pre_color
if x < 0 or x >= n or y < 0 or y >= n:
return False
# print(x, y, ':', graph[x][y], pre_color)
# 지정 범위 내에 현재 컬러를 저장해둔다.
tmp_color = graph[x][y]
if graph[x][y] == pre_color and visited[x][y] == False:
# 지나간 자리는 블랙으로 색칠해준다.
visited[x][y] = True
for i in range(4):
nx = x + dx[i]
ny = y + dy[i]
pre_color = tmp_color
dfs(nx, ny, graph)
return True
return False
# 색을 판단해주는 함수
def add_color_chunk(x, y, graph):
global red, green, blue
if graph[x][y] == 'R':
red += 1
return
elif graph[x][y] == 'G':
green += 1
return
elif graph[x][y] == 'B':
blue += 1
return
# 입력
n = int(input())
graph_3color = [list(map(str, input())) for _ in range(n)]
# 각 칼라 구역의 갯수를 나타낸다.
red, green, blue = 0, 0, 0
# DFS에서 상하좌우 움직이기 위한 변수
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
# 방문했는지 보여준다.
visited = [[False] * n for _ in range(n)]
# 색약 없는 사람
for i in range(n):
for j in range(n):
pre_color = graph_3color[i][j]
if dfs(i, j, graph_3color):
add_color_chunk(i, j, graph_3color)
# print(red, green, blue)
print(red + green + blue, end=" ")
# 색약이 보는 그래프 만들어주기
# 색약 있는 사람(녹색을 빨간색으로 본다고 가정한다.)
graph_2color = graph_3color
for i in range(n):
for j in range(n):
if graph_2color[i][j] == 'G':
graph_2color[i][j] = 'R'
# 각 칼라 구역의 갯수를 다시 리셋해준다.
red, green, blue = 0, 0, 0
# 방문 기록도 다시 리셋해준다.
visited = [[False] * n for _ in range(n)]
# 색약 있는 사람
for i in range(n):
for j in range(n):
pre_color = graph_2color[i][j]
if dfs(i, j, graph_2color):
add_color_chunk(i, j, graph_2color)
# print(red, green, blue)
print(red + green + blue)
이것을 BFS로 구현한다면
아래와 같다.단, 이 코드는 python3로는 시간초과가 뜨고,pypy3로는 정답 코드로 나오게 된다. 깔끔하다.왜 런타임 에러가 안 될까 최단거리 찾기 문제 말고도어떤 상황 때 DFS보다 BFS가 효율적일 수 있는 것일까.
from collections import deque
def bfs(x, y):
queue.append((x, y))
done.append((x, y))
while queue:
curr = queue.popleft()
for i in range(4):
nx = curr[0] + dx[i]
ny = curr[1] + dy[i]
if (0 <= nx < N) and (0 <= ny < N) and ((nx, ny) not in done):
if colors[nx][ny] == colors[curr[0]][curr[1]]:
queue.append((nx, ny))
done.append((nx, ny))
N = int(input())
colors = [list(input()) for _ in range(N)]
dx = [1, -1, 0, 0]
dy = [0, 0, 1, -1]
# 정상인인 경우
queue = deque()
done = []
cnt_1 = 0
for i in range(N):
for j in range(N):
if (i, j) not in done:
bfs(i, j)
cnt_1 += 1
# 적록색맹인 경우
for i in range(N):
for j in range(N):
if colors[i][j] == 'G':
colors[i][j] = 'R'
queue = deque()
done = []
cnt_2 = 0
for i in range(N):
for j in range(N):
if (i, j) not in done:
bfs(i, j)
cnt_2 += 1
print(cnt_1, cnt_2)
'Computer Science > Algorithm' 카테고리의 다른 글
[백준] 7576번 토마토 상자 - 시간초과.. BFS 활용하자 (0) | 2023.02.20 |
---|---|
list, dict, set은 mutable 하다. Shallow Copy & Deep Copy (0) | 2023.02.20 |
약점보완: Dynamic Programming (0) | 2023.02.09 |
[백준] 2164번 카드2 - 시간초과, 짝수 홀수 인덱스만 추출하기 (0) | 2023.02.02 |
[백준] 10816번 숫자카드2, 시간초과 - 이분탐색만으로도 안됨, bisect 모듈! (0) | 2023.01.25 |