[Gold III]
https://www.acmicpc.net/problem/17142
이 문제는... 사실 토마토나, 백조의 호수 같은 문제와 유사한 문제였으나, 시간이 조금 걸렸다.
문제를 푸는 핵심은, '비활성화된 바이러스 또한 전염이 진행될 수 있도록 처리할 것.'
그리고, '비활성화된 바이러스가 활성화되는 것은 최소 시간을 측정하는 데에 고려하지 않아야 할 것.'
이것들을 유념하며 문제를 풀어 나가야 한다.
풀이 1
일단 BFS 문제라는 것은 보자마자 알 수 있었다.
다만, 시간제한이 좀 빠듯할 것 같다는 생각이 들었는데...
바이러스를 놓을 수 있는 칸은 총 (M~10)개가 있고, 놓을 수 있는 바이러스의 개수가 M(1~10)개.
이 경우들을 모두 Brute-Force로 따져서 하나하나 BFS를 돌려가며 계산해보는 방법밖엔 없어서였는데,
막상 최대 갯수를 계산해 보면 10C5이므로 252개. 그리 많지는 않은 개수라 일단 안심하고 들어갔다.
모두 진행되었는지 등의 과정을 반복문 안에 막 집어넣어서 그런지 TLE 날 것이라 예상하긴 했음.
TLE.
from collections import deque
from itertools import combinations
import copy
# input
N, M = map(int, input().split())
lab = []
for _ in range(N):
lab.append(list(map(int, input().split())))
# delta row, delta col.
dr = [1, 0, -1, 0]
dc = [0, 1, 0, -1]
# find 2 in laboratory.
twos = []
for r, row in enumerate(lab):
for c, i in enumerate(row):
if i == 2:
twos.append([r, c])
# Combination - (len of twos) C (M) - find all combination of index.
start = list(combinations(twos, M))
# save Minimum Second to infect all blocks.
minSec = 10000
def isAllInfected(inLab):
# function to know all blocks infected.
for row in inLab:
for c in row:
if c == 0:
return False
return True
# check if minSec = 0
if isAllInfected(lab):
print(0)
exit()
# cycle of combinations
for s in start:
sec = 0
q = deque(s)
templab = copy.deepcopy(lab)
# change numbers (wall, virus)
for i in range(N):
for j in range(N):
if templab[i][j] == 2:
templab[i][j] = -1
elif templab[i][j] == 1:
templab[i][j] = -1
# BFS - one cycle (1 case)
while q:
sec += 1
tempq = deque()
# BFS - one cycle (1 sec)
while q:
r, c = q.popleft()
for i in range(4):
nr = r + dr[i]
nc = c + dc[i]
# check if wall / if virus already infected.
if N > nr >= 0 and N > nc >= 0 and templab[nr][nc] == 0:
tempq.append([nr, nc])
# save second for infect this block.
templab[nr][nc] = sec
q = copy.deepcopy(tempq)
if isAllInfected(templab):
result = max(map(max, templab))
if minSec > result:
minSec = result
if minSec != 10000:
print(minSec)
else:
print(-1)
풀이 2
1, 2와 같은 숫자를 바꾸는 과정(O(N^2))을 반복문 안에 집어넣을 필요가 없어 제거하고,
copy.deepcopy()의 사용을 줄이고,
처음부터 빈칸이 없다면 예외처리 하는 등 여러 부분을 개선.
그러나 WA.
from collections import deque
from itertools import combinations
import copy
# input
N, M = map(int, input().split())
lab = []
for _ in range(N):
lab.append(list(map(int, input().split())))
# delta row, delta col.
dr = [1, 0, -1, 0]
dc = [0, 1, 0, -1]
# find 2 in laboratory / count 0 in lab / change numbers(wall, virus)
twos = []
cnt_0 = 0
for r, row in enumerate(lab):
for c, i in enumerate(row):
if i == 2:
twos.append([r, c])
lab[r][c] = "*"
elif i == 1:
lab[r][c] = "-"
elif i == 0:
cnt_0 += 1
# Combination - (len of twos) C (M) - find all combination of index.
start = list(combinations(twos, M))
# save Minimum Second to infect all blocks.
minSec = 1000000
def isAllInfected(inLab):
# function to know all blocks infected.
for row in inLab:
for c in row:
if c == 0:
return False
return True
# check if minSec = 0
if isAllInfected(lab):
print(0)
exit()
# cycle of combinations
for s in start:
sec = 0
cnt_change = 0
q = deque(s)
templab = copy.deepcopy(lab)
# BFS - one cycle (1 case)
while q:
sec += 1
tempq = deque()
# BFS - one cycle (1 sec)
while q:
r, c = q.popleft()
for i in range(4):
nr = r + dr[i]
nc = c + dc[i]
# check if wall / if virus already infected.
if N > nr >= 0 and N > nc >= 0 and templab[nr][nc] == 0:
tempq.append([nr, nc])
# save second for infect this block.
templab[nr][nc] = sec
cnt_change += 1
q = tempq
if cnt_change == cnt_0:
result = sec - 1
if minSec > result:
minSec = result
# print("-------")
# for k in templab:
# print(*k)
if minSec != 10000:
print(minSec)
else:
print(-1)
풀이 3
풀이 2에서는, 비활성화된 바이러스가 활성화되지 못하도록 코드가 짜여짐.
그래서 이를 개선했음. 그러나, 비활성화된 바이러스가 활성화되는 것은 최소 시간을 측정하는 데에 영향이 가지 않아야 하나, (빈칸만 다 채워지면 ok.) 그것을 고려하지 못함.
93%에서 WA.
from collections import deque
from itertools import combinations
import copy
# input
N, M = map(int, input().split())
lab = []
for _ in range(N):
lab.append(list(map(int, input().split())))
# delta row, delta col.
dr = [1, 0, -1, 0]
dc = [0, 1, 0, -1]
# find 2 in laboratory / count 0 in lab / change numbers(wall, virus)
twos = []
cnt_0 = 0
for r, row in enumerate(lab):
for c, i in enumerate(row):
if i == 2:
twos.append([r, c])
lab[r][c] = "*"
elif i == 1:
lab[r][c] = "-"
elif i == 0:
cnt_0 += 1
# Combination - (len of twos) C (M) - find all combination of index.
start = list(combinations(twos, M))
# save Minimum Second to infect all blocks.
minSec = 1000000
def isAllInfected(inLab):
# function to know all blocks infected.
for row in inLab:
for c in row:
if c == 0:
return False
return True
# check if minSec = 0
if isAllInfected(lab):
print(0)
exit()
# cycle of combinations
for s in start:
sec = 0
cnt_change = 0
q = deque(s)
templab = copy.deepcopy(lab)
# BFS - one cycle (1 case)
while q:
sec += 1
tempq = deque()
# BFS - one cycle (1 sec)
while q:
r, c = q.popleft()
for i in range(4):
nr = r + dr[i]
nc = c + dc[i]
# check if wall / if virus already infected.
if N > nr >= 0 and N > nc >= 0 and (templab[nr][nc] == 0 or templab[nr][nc] == "*"):
flag = False
# if virus activated, 주변에 다른 바이러스나 빈칸 확인.
if templab[nr][nc] == "*":
for k in range(4):
kr = nr + dr[i]
kc = nc + dc[i]
if N > kr >= 0 and N > kc >= 0 and (templab[kr][kc] == "*" or templab[kr][kc] == 0):
flag = True
else:
flag = True
if flag:
tempq.append([nr, nc])
if templab[nr][nc] == 0:
cnt_change += 1
# save second for infect this block.
templab[nr][nc] = sec
q = tempq
if cnt_change == cnt_0:
result = sec - 1
if minSec > result:
minSec = result
# print("-------")
# for k in templab:
# print(*k)
if minSec != 1000000:
print(minSec)
else:
print(-1)
풀이 4
활성화된 바이러스가 숫자로 바뀌었으므로,
그 칸의 숫자가 max값을 구하는 데에 이용되지 않도록 처리해주어야 함.
line 54~55 참고.
추가로, 모든 빈칸에 바이러스가 퍼졌는지 계산하는 과정을 풀이 1과 같이 다시 반복문 안에 넣음. 다행히 TLE나지 않고 잘 진행.
드디어 AC!!!
from collections import deque
from itertools import combinations
import copy
# input
N, M = map(int, input().split())
lab = []
for _ in range(N):
lab.append(list(map(int, input().split())))
# delta row, delta col.
dr = [1, 0, -1, 0]
dc = [0, 1, 0, -1]
# find 2 in laboratory / count 0 in lab / change numbers(wall, virus)
twos = []
cnt_0 = 0
for r, row in enumerate(lab):
for c, i in enumerate(row):
if i == 2:
twos.append([r, c])
lab[r][c] = "*"
elif i == 1:
lab[r][c] = "-"
elif i == 0:
cnt_0 += 1
# Combination - (len of twos) C (M) - find all combination of index.
start = list(combinations(twos, M))
# save Minimum Second to infect all blocks.
minSec = 1000000
def isAllInfected(inLab):
# function to know all blocks infected.
for row in inLab:
for c in row:
if c == 0:
return False
return True
# check if minSec = 0
if isAllInfected(lab):
print(0)
exit()
def check(inLab):
count0 = 0 # 0의 갯수 확인
maxCount = 0 # 최단 거리 계산
for two in twos:
inLab[two[0]][two[1]] = "*"
for i in range(N):
for j in range(N):
if inLab[i][j] == 0:
return -1
if inLab[i][j] != '*' and inLab[i][j] != '-':
maxCount = max(maxCount, inLab[i][j])
if count0 == 0:
return maxCount
# cycle of combinations
for s in start:
sec = 0
q = deque(s)
templab = copy.deepcopy(lab)
# BFS - one cycle (1 case)
while q:
sec += 1
tempq = deque()
# BFS - one cycle (1 sec)
while q:
r, c = q.popleft()
for i in range(4):
nr = r + dr[i]
nc = c + dc[i]
# check if wall / if virus already infected.
if N > nr >= 0 and N > nc >= 0 and (templab[nr][nc] == 0 or templab[nr][nc] == "*"):
tempq.append([nr, nc])
# save sec for infect this block.
templab[nr][nc] = sec
q = tempq
a = check(templab)
if a != -1 and minSec > a:
minSec = a
# print("-------")
# for k in templab:
# print(*k)
if minSec != 1000000:
print(minSec)
else:
print(-1)