2606번: 바이러스
첫째 줄에는 컴퓨터의 수가 주어진다. 컴퓨터의 수는 100 이하인 양의 정수이고 각 컴퓨터에는 1번 부터 차례대로 번호가 매겨진다. 둘째 줄에는 네트워크 상에서 직접 연결되어 있는 컴퓨터 쌍
www.acmicpc.net
문제
신종 바이러스인 웜 바이러스는 네트워크를 통해 전파된다. 한 컴퓨터가 웜 바이러스에 걸리면 그 컴퓨터와 네트워크 상에서 연결되어 있는 모든 컴퓨터는 웜 바이러스에 걸리게 된다.
예를 들어 7대의 컴퓨터가 <그림 1>과 같이 네트워크 상에서 연결되어 있다고 하자. 1번 컴퓨터가 웜 바이러스에 걸리면 웜 바이러스는 2번과 5번 컴퓨터를 거쳐 3번과 6번 컴퓨터까지 전파되어 2, 3, 5, 6 네 대의 컴퓨터는 웜 바이러스에 걸리게 된다. 하지만 4번과 7번 컴퓨터는 1번 컴퓨터와 네트워크상에서 연결되어 있지 않기 때문에 영향을 받지 않는다.

어느 날 1번 컴퓨터가 웜 바이러스에 걸렸다. 컴퓨터의 수와 네트워크 상에서 서로 연결되어 있는 정보가 주어질 때, 1번 컴퓨터를 통해 웜 바이러스에 걸리게 되는 컴퓨터의 수를 출력하는 프로그램을 작성하시오.
입력
첫째 줄에는 컴퓨터의 수가 주어진다. 컴퓨터의 수는 100 이하인 양의 정수이고 각 컴퓨터에는 1번 부터 차례대로 번호가 매겨진다. 둘째 줄에는 네트워크 상에서 직접 연결되어 있는 컴퓨터 쌍의 수가 주어진다. 이어서 그 수만큼 한 줄에 한 쌍씩 네트워크 상에서 직접 연결되어 있는 컴퓨터의 번호 쌍이 주어진다.
출력
1번 컴퓨터가 웜 바이러스에 걸렸을 때, 1번 컴퓨터를 통해 웜 바이러스에 걸리게 되는 컴퓨터의 수를 첫째 줄에 출력한다.
풀이
import sys
input = sys.stdin.readline
n = int(input()) # 컴퓨터의 수 n
m = int(input()) # 연결된 컴퓨터 쌍의 수 m
li = [[] for _ in range(n + 1)] # 정점 간 연결 정보를 입력받을 리스트 li
visited = [False] * (n + 1) # 방문 여부를 표시할 리스트
# 정점 간 연결 정보 입력받기
for _ in range(m):
a, b = map(int, input().split())
li[a].append(b)
li[b].append(a)
#재귀함수로 dfs 구현
def dfs(v):
visited[v] = True
for i in li[v]:
if not visited[i]:
dfs(i)
dfs(1)
print(visited.count(True)-1)
#1번 컴퓨터는 카운트에서 제외하므로 1을 빼고 True로 바뀐 개수를 출력함
재귀함수를 이용한 dfs로 풀이하였습니다.
정점 간 연결 정보 입력받기
# 정점 간 연결 정보 입력받기
for _ in range(m):
a, b = map(int, input().split())
li[a].append(b)
li[b].append(a)
- li(정점 간 연결 정보) 리스트는 아래 예시와 같이, 1번 인덱스에는 정점 1과 연결된 정점을, 2번 인덱스에는 정점 2와 연결된 정점을 리스트 형태로 표현하도록 입력받았습니다.
- 인덱스 번호와 리스트를 맞추었기 때문에 0번 인덱스는 빈 배열입니다.
li = [
[], # 더미(dummy) 값
[2, 3, 4], # 정점 1과 연결된 정점들: 2, 3, 4
[4], # 정점 2와 연결된 정점: 4
[4], # 정점 3과 연결된 정점: 4
[] # 정점 4와 연결된 정점들: (없음, 고립된 정점)
]
재귀함수로 dfs 구현
#재귀함수로 dfs 구현
def dfs(v):
visited[v] = True
for i in li[v]:
if not visited[i]:
dfs(i)
- dfs라는 함수를 정의하였고, 하나의 정점을 확인할 때 마다 visited 배열을 True로 바꾸면서 방문 여부를 표시했습니다.
- 현재 보고 있는 정점과 연결된 정점을 for문을 돌며 모두 확인하면서, 방문하지 않았다면 재귀함수를 통해 연결된 정점을 방문하는 형태로 dfs를 구현했습니다.
결과 출력
dfs(1)
print(visited.count(True)-1)
#1번 컴퓨터는 카운트에서 제외하므로 1을 빼고 True로 바뀐 개수를 출력함
1번 컴퓨터가 바이러스에 걸렸으므로 무조건 시작 지점은 1이기 때문에 dfs(1)을 해주고,
visited에서 True로 바뀐 곳만 방문, 즉 바이러스에 걸렸으므로 True를 카운트해주었습니다.
단, 1번 컴퓨터는 세지 않으므로 1번 컴퓨터를 제외한 개수를 count해서 출력합니다.
깊이 우선 탐색(DFS)란?
- 깊이 우선 탐색(DFS) 그래프나 트리 구조에서 모든 노드를 방문하고자 할 때 사용되는 탐색 알고리즘
- DFS는 한 지점에서 시작하여 다음 지점으로 이동할 때 최대한 깊숙히 들어가서 더 이상 갈 곳이 없을 때까지 탐색하고, 그 후에 백트래킹하여 다른 가능한 경로를 탐색한다. 이를 재귀 함수나 스택을 활용하여 구현할 수 있다.
DFS의 주요 특징
- 스택 또는 재귀 함수를 사용하여 구현
- 한 노드에서 가능한 한 깊게 들어가서 다음 노드를 탐색
- 깊은 경로를 탐색한 후에는 백트래킹을 통해 다른 경로를 시도
파이썬에서의 DFS 구현 예제
- 그래프를 인접 리스트로 표현하고 DFS를 사용하여 그래프를 탐색
# 그래프를 인접 리스트로 표현
graph = {
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B', 'F'],
'F': ['C', 'E']
}
visited = {} # 방문 여부를 저장할 딕셔너리
def dfs(node):
if node not in visited:
print(node, end=' ') # 노드를 방문한 것으로 표시하고 출력
visited[node] = True
for neighbor in graph[node]:
dfs(neighbor) # 이웃 노드를 재귀적으로 방문
# 시작 노드 'A'에서 DFS 시작
dfs('A')
이 코드는 'A' 노드에서 시작하여 DFS로 그래프를 탐색한다. 방문한 노드를 출력하고 방문 여부를 딕셔너리로 관리하여 무한 루프를 방지한다.
'개발공부 > 코테' 카테고리의 다른 글
[백준 17608: 막대기] 파이썬 풀이 _ 스택(stack) (3) | 2023.09.03 |
---|---|
[백준 1057번: 토너먼트] 파이썬 풀이 _ 브루트포스 알고리즘 (1) | 2023.09.02 |
[백준 2217번: 로프] 파이썬 풀이 (0) | 2023.08.13 |
[백준 19941번: 햄버거 분배] 파이썬 풀이 (1) | 2023.08.12 |
[백준 4796번: 캠핑] 파이썬 풀이_그리디 알고리즘 (1) | 2023.08.10 |