✍️오늘 공부한 내용
📋새로 배우게 된 내용
- 오늘은 bfs와 dfs로 풀이 가능한 문제들을 많이 접했다. 문제들을 풀다보니 어느정도
패턴
(?)이 생기는 거 같디. 아직 문제를 도움없이 풀기 쉽지 않지만 이 패턴을 염두해두고 코드를 짜놓으면 좋을 거 같다.
- 무한대를 표현하기 위해
1e9,-1e9
사용
- 구하라는 건 다르기때문에 응용을 해야겠지만 기본틀은 어느정도 비슷한 느낌을 받았다.
- 알고 있었는데, 오늘 문제들을 풀면서 더 뼈져리게 느꼈다. ☠️ input()보다는
sys.stdin.readline().strip()
을 사용하는 습관을 들이자! (시간초과 가능성 줄임)
😜bfs , dfs 문제 풀이 방식 공통
- vertex 방문 graph만들어주기
- 방문여부 그래프 만들어주기
- edge개수만큼 연결 상태 나타내기
- 함수 선언하고 vertex인덱스 인자로 받기
- 큐(bfs) 또는 재귀함수(dfs)로 방문여부 반영
🪄bfs 문제 풀이패턴
- vertex를 방문했는지 여부를 나타낼 그래프를 만든다 (0,1로 연결 상태 표시)
graph = [[0] * (n+1) for _ in range(n+1)]
- 방문여부 그래프를 만들기
visited = [False] * (n+1)
- edge개수만큼 연결 상태 표시하기
for _ in range(m): graph[a][b] = graph[b][a] = 1
- bfs함수 선언하고 search를 시작할 vertex를 인자로 받아온다.
- deque 라이브러리를 활용해서 큐를 만들고, 큐에 아무것도 없을때까지 첫번째 item을 popleft()해준다.
while queue: start = queue.popleft()
- 1~n번 vertex를 돌면서 방문은 안했는데 연결한 vertex를 큐에 추가해주고 방문했다고 표시해주기.
- 첫번째 ~ n번째 vertex까지 for문 돌면서 i번쨰 방문여부 확인
if not visited[i]:BFS(i) count+=1
하고, 방문하지 않은 경우 BFS를 실행
🐰 11724번 bfs
- 해당 문제는 연결된 요소를 확인하는 문제**라서 추가적으로 방문하지 않은 vertex가 생길때 (==bfs를 실행할때==edge로연결이 되어있지 않을 때)를 카운트해줌
from sys import stdin
from collections import deque
n, m = map(int, stdin.readline().split(' '))
graph = [[0] * (n+1) for _ in range(n+1)]
visited = [False] * (n+1)
for _ in range(m):
a, b = map(int, stdin.readline().split(' '))
graph[a][b] = graph[b][a] = 1
def BFS(start):
queue = deque([start])
while queue:
start = queue.popleft()
for i in range(1, n+1):
print(i,start,visited[i],graph[start][i])
if not visited[i] and graph[start][i] == 1:
queue.append(i)
visited[i] = True
count = 0
for i in range(1, n+1):
if not visited[i]:
BFS(i)
count += 1
print(count)
🐰 7569번 bfs
import sys
from collections import deque
m, n, h = map(int, input().split())
graph = []
queue = deque([])
for i in range(h):
tmp = []
for j in range(n):
tmp.append(list(map(int, sys.stdin.readline().split())))
for k in range(m):
if tmp[j][k] == 1:
queue.append([i, j, k])
graph.append(tmp)
dx = [-1, 1, 0, 0, 0, 0]
dy = [0, 0, 1, -1, 0, 0]
dz = [0, 0, 0, 0, 1, -1]
while (queue):
x, y, z = queue.popleft()
for i in range(6):
a = x+dx[i]
b = y+dy[i]
c = z+dz[i]
if 0 <= a < h and 0 <= b < n and 0 <= c < m and graph[a][b][c] == 0:
queue.append([a, b, c])
graph[a][b][c] = graph[x][y][z]+1
day = 0
for i in graph:
for j in i:
for k in j:
if k == 0:
print(-1)
exit(0)
day = max(day, max(j))
print(day-1)
🪄 dfs 문제 풀이패턴
setrecursionlimit
으로 재귀 깊이를 최대한 깊게 해서 recursion error 방지
2 ~ 3은 bfs와 동일함!
dfs함수 선언
하고 감색 시작 vertex를 인자로 넘겨준다.
- queue로 구현했던 bfs와 달리
재귀
를 통해 if not visited[i] and graph[start][i] == 1:DFS(i)
방문 여부를 표시해주기
🦕 11724번 dfs
import sys
sys.setrecursionlimit(10000)
n, m = map(int, sys.stdin.readline().split(' '))
graph = [[0] * (n+1) for _ in range(n+1)]
visited = [False] * (n+1)
print("visited",visited)
for _ in range(m):
a, b = map(int, sys.stdin.readline().split(' '))
graph[a][b] = graph[b][a] = 1
print(graph)
def DFS(start):
visited[start] = True
for i in range(1, n+1):
if not visited[i] and graph[start][i] == 1:
DFS(i)
count = 0
for i in range(1, n+1):
if not visited[i]:
DFS(i)
count += 1
print(count)
🦕 14888번 dfs
from sys import stdin
n = int(stdin.readline().strip())
data = list(map(int,stdin.readline().strip().split()))
add,sub,mul,div=map(int,stdin.readline().strip().split())
min_val,max_val= 1e9,-1e9
def dfs(i,arr):
global add,sub,mul,div,max_val,min_val
if i==n:
max_val=max(max_val,arr)
min_val=min(min_val,arr)
else:
if add>0:
add-=1
dfs(i+1,arr+data[i])
add+=1
if sub>0:
sub-=1
dfs(i+1,arr-data[i])
sub+=1
if mul>0:
mul-=1
dfs(i+1,arr*data[i])
mul+=1
if div>0:
div-=1
dfs(i+1,int(arr/data[i]))
div+=1
dfs(1,data[0])
print(max_val)
print(min_val)
🫥오해했던 점
- deque를 쓰면 시간복잡도가 줄어든다는 건 알고 있었다. leftpop시 O(logN)인데 list로 구현 시 하나씩 밀어줘야하기 때문에 시간복잡도가 O(N)이 되버린다는 걸 직접 코드로 구현해봤다. 백준에서 다시 코드를 돌려봤는데..어? 실행시간이 더 기내🤔
- 눈으로 직접보는 걸 좋아해서 시간복잡도도 시각화할 수 없을까 고민하다가 시간복잡도를 계산할 수 있도록 코드를 짤 수 있다는 걸 알게 되었다. 다음부터는 아래의 자료를 참고해서 적용해봐야겠다!
👉시간복잡도 계산해보기
🤯어려웠던 점
- 아직 고민중인 부분인데, 이론 공부를 하면서 graph로 방문여부를 나타내거나 edge를 표현하면 많은 공간낭비가 일어날 수 있다는 걸 배웠었다. 실제로도 코드를 찍어보니, vertice간의 연결이 별로 없는 경우 불필요하느 공간을 만들었고 쓰이지 않았다. 메모리는 최대한 효율적으로 써야 컴퓨터도 프로세스하기 편하고 나도 오류가 적을텐데, 어떻게 이 문제를 해결할까?
😸어려움을 해결한 방법
- 다익스트라를 공부하면서 dict형태로 edge상태를 표현할 수 있다는 걸 배웠었는데 현재 11724번 문제에 적용 중인데 시간초과가 난다. 아마도 다익스트라는 최소비용을 구하는 문제라 heapq로 우선순위를 따지는 문젠데 edge관계를 구하라는 11724번 문제와는 좀 거리가 있지 않나 생각이 들었다.
graph = {
'A': {'B': 2, 'C': 6},
'B': {'D': 5},
'C': {'D': 8},
'D': {},
}
- 시간초과를 해결하기 위해 좀 더 고민해봐야겠다. list대신 deque사용해본다던가🤔
🤔아쉬웠던 점
- 내가 직접 짠 코드보다는 남의 코드를 보면서 공부한 시간이 많은 날이었다..아쉬운 건 내가 직접짜고 싶은 욕심(?)이 좀 났다는 점. 그래도 이런 식으로 코드를 짤 수 있구나 배울 수 있어서 좋았다.
😝느낀점
- 패턴이 어느정도 보이니까 이거에 맞게 문제에 접근하면서 연습을 하다보면 어려운 문제도 풀 수 있을 거 같아서 희망찬(?) 느낌이 들었다.
👊다짐
🚀오늘의 한줄평