제로베이스 20220511 코테 연습
1. 바이러스
- 신종 바이러스인 웜 바이러스는 네트워크를 통해 전파된다.
-
한 컴퓨터가 웜 바이러스에 걸리면 그 컴퓨터와 네트워크 상에서 연결되어 있는 모든 컴퓨터는 웜 바이러스에 걸리게 된다.
- 예를 들어 7대의 컴퓨터가 <그림 1>과 같이 네트워크 상에서 연결되어 있다고 하자.
- 1번 컴퓨터가 웜 바이러스에 걸리면 웜 바이러스는 2번과 5번 컴퓨터를 거쳐 3번과 6번 컴퓨터까지 전파되어 2, 3, 5, 6 네 대의 컴퓨터는 웜 바이러스에 걸리게 된다.
- 하지만 4번과 7번 컴퓨터는 1번 컴퓨터와 네트워크상에서 연결되어 있지 않기 때문에 영향을 받지 않는다.
- 어느 날 1번 컴퓨터가 웜 바이러스에 걸렸다. 컴퓨터의 수와 네트워크 상에서 서로 연결되어 있는 정보가 주어질 때, 1번 컴퓨터를 통해 웜 바이러스에 걸리게 되는 컴퓨터의 수를 출력하는 프로그램을 작성하시오.
입력
- 첫째 줄에는 컴퓨터의 수가 주어진다.
- 컴퓨터의 수는 100 이하이고 각 컴퓨터에는 1번 부터 차례대로 번호가 매겨진다.
- 둘째 줄에는 네트워크 상에서 직접 연결되어 있는 컴퓨터 쌍의 수가 주어진다.
- 이어서 그 수만큼 한 줄에 한 쌍씩 네트워크 상에서 직접 연결되어 있는 컴퓨터의 번호 쌍이 주어진다.
출력
- 1번 컴퓨터가 웜 바이러스에 걸렸을 때, 1번 컴퓨터를 통해 웜 바이러스에 걸리게 되는 컴퓨터의 수를 첫째 줄에 출력한다.
풀이
import sys
def dfs(x, visited):
if x not in visited:
visited.append(x)
for k in graph[x]:
if k != N + 1:
dfs(k, visited)
return visited
N = int(sys.stdin.readline())
graph = [[N + 1] * N for _ in range(0, N)]
for _ in range(int(sys.stdin.readline())):
a, b = [int(x) for x in sys.stdin.readline().strip('\n').split()]
graph[a - 1][b - 1] = min(graph[a - 1][b - 1], b - 1)
graph[b - 1][a - 1] = min(graph[b - 1][a - 1], a - 1)
print(len(dfs(0, []))-1)
채점 결과
- 정답
리뷰
-
말이 많지만 결국 그래프를 그리고, 1번 컴퓨터에 해당하는 정점과 연결된 모든 정점의 개수를 반환하는 문제이다.
-
dfs 사용해서 1번 정점에 연결된 모든 정점을 return하고, 그 개수에서 1 빼주어(자기 자신은 포함 안하는것을 예제로 확인 가능했다.) 답을 도출했다.
2. 집합
-
비어있는 공집합 S가 주어졌을 때, 아래 연산을 수행하는 프로그램을 작성하시오.
- add x: S에 x를 추가한다. (1 ≤ x ≤ 20) S에 x가 이미 있는 경우에는 연산을 무시한다.
- remove x: S에서 x를 제거한다. (1 ≤ x ≤ 20) S에 x가 없는 경우에는 연산을 무시한다.
- check x: S에 x가 있으면 1을, 없으면 0을 출력한다. (1 ≤ x ≤ 20)
- toggle x: S에 x가 있으면 x를 제거하고, 없으면 x를 추가한다. (1 ≤ x ≤ 20)
- all: S를 {1, 2, …, 20} 으로 바꾼다.
- empty: S를 공집합으로 바꾼다.
입력
-
첫째 줄에 수행해야 하는 연산의 수 M (1 ≤ M ≤ 3,000,000)이 주어진다.
-
둘째 줄부터 M개의 줄에 수행해야 하는 연산이 한 줄에 하나씩 주어진다.
출력
- check 연산이 주어질때마다, 결과를 출력한다.
풀이
import sys
order_dic = {
"add": lambda S, x: S | {x} if not S & {x} else S,
"remove": lambda S, x: S - {x} if S & {x} else S,
"check": lambda S, x: S if print(1 if S & {x} else 0) else S,
"toggle": lambda S, x: S | {x} if not S & {x} else S-{x},
"all": lambda S: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
"empty": lambda S: set()
}
S = set()
for _ in range(int(sys.stdin.readline())):
order = sys.stdin.readline().strip('\n').split()
if len(order) == 2:
S = order_dic[order[0]](S, int(order[1]))
else:
S = order_dic[order[0]](S)
채점 결과
- 정답
리뷰
-
결과만 놓고 보면, 고수인척 한번 해보려고 딕셔너리 + lambda + set함수를 통한 집합 연산을 도입했다가 괜히 시간만 겁나게 잡아먹은 바보같은 풀이였다.
-
이번 문제에서 이런 방식을 쓰면 안되었던 이유를 위주로 설명하자면, 다음과 같다.
- 딕셔너리 + 람다식 사용
- = 기호의 사용 불가로 인해 간단한 등호나 명령문으로 끝날 문제가 화려하게 꼬여버렸다.
- Set함수를 통한 집합연산
- 위의 딕셔너리/람다식 문제의 연장선인데, 위 문제로 인해 리스트에 뭘 넣거나 빼는 연산이 힘들어져 Set을 사용하게 되었고, 불필요한 교집합 연산등을 사용하게 되면서 시간 복잡도가 미친듯이 늘어났다.
- 딕셔너리 + 람다식 사용
-
이번 문제의 교훈
- 나대지 말고, 기본기에 집중하자.
더 나은 풀이
- 풀이 출처 : 아이디 mphtlee님의 풀이
import sys
l = []
for _ in range(int(sys.stdin.readline())):
comm = sys.stdin.readline().split()
if comm[0] == "add":
if comm[1] in l:
continue
else:
l.append(comm[1])
if comm[0] == "remove":
if comm[1] in l:
l.remove(comm[1])
if comm[0] == "check":
sys.stdout.write('1\n' if comm[1] in l else '0\n')
if comm[0] == "toggle":
if comm[1] in l:
l.remove(comm[1])
else:
l.append(comm[1])
if comm[0] == "all":
# for speed reason, not using for loop list
l = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20']
if comm[0] == "empty":
l = []
더 나은 풀이 리뷰
-
기본에 충실하되, 군데군데에서 최적화를 위해 노력한 흔적들이 보였다.
-
int로 변환하는 함수조차 사용하지 않기위한 문자열형식에서의 처리
-
시간 이슈 해결을 위해 all함수를 루프를 사용하지 않고 직접 정의한 것.
-
sys.stdout.write()함수를 사용하여 print문에 비해 시간을 절약함.
-