728x90
# 조건
[본 문제는 정확성과 효율성 테스트 각각 점수가 있는 문제입니다.]
- N x M 크기의 행렬 모양의 게임 맵이 있습니다.
- 이 맵에는 내구도를 가진 건물이 각 칸마다 하나씩 있습니다.
- 적은 이 건물들을 공격하여 파괴하려고 합니다.
- 건물은 적의 공격을 받으면 내구도가 감소하고 내구도가 0이하가 되면 파괴됩니다. 반대로, 아군은 회복 스킬을 사용하여 건물들의 내구도를 높이려고 합니다.
- 적의 공격과 아군의 회복 스킬은 항상 직사각형 모양입니다.
- 예를 들어, 아래 사진은 크기가 4 x 5인 맵에 내구도가 5인 건물들이 있는 상태입니다.
- 첫 번째로 적이 맵의 (0,0)부터 (3,4)까지 공격하여 4만큼 건물의 내구도를 낮추면 아래와 같은 상태가 됩니다.
- 두 번째로 적이 맵의 (2,0)부터 (2,3)까지 공격하여 2만큼 건물의 내구도를 낮추면 아래와 같이 4개의 건물이 파괴되는 상태가 됩니다.
- 세 번째로 아군이 맵의 (1,0)부터 (3,1)까지 회복하여 2만큼 건물의 내구도를 높이면 아래와 같이 2개의 건물이 파괴되었다가 복구되고 2개의 건물만 파괴되어있는 상태가 됩니다.
- 마지막으로 적이 맵의 (0,1)부터 (3,3)까지 공격하여 1만큼 건물의 내구도를 낮추면 아래와 같이 8개의 건물이 더 파괴되어 총 10개의 건물이 파괴된 상태가 됩니다. (내구도가 0 이하가 된 이미 파괴된 건물도, 공격을 받으면 계속해서 내구도가 하락하는 것에 유의해주세요.)
- 최종적으로 총 10개의 건물이 파괴되지 않았습니다.
- 건물의 내구도를 나타내는 2차원 정수 배열
board
와 적의 공격 혹은 아군의 회복 스킬을 나타내는 2차원 정수 배열skill
이 매개변수로 주어집니다. - 적의 공격 혹은 아군의 회복 스킬이 모두 끝난 뒤 파괴되지 않은 건물의 개수를 return하는 solution함수를 완성해 주세요.
제한사항
- 1 ≤
board
의 행의 길이 (=N
) ≤ 1,000 - 1 ≤
board
의 열의 길이 (=M
) ≤ 1,000 - 1 ≤
board
의 원소 (각 건물의 내구도) ≤ 1,000 - 1 ≤
skill
의 행의 길이 ≤ 250,000 skill
의 열의 길이 = 6skill
의 각 행은[type, r1, c1, r2, c2, degree]
형태를 가지고 있습니다.- type은 1 혹은 2입니다.
- type이 1일 경우는 적의 공격을 의미합니다. 건물의 내구도를 낮춥니다.
- type이 2일 경우는 아군의 회복 스킬을 의미합니다. 건물의 내구도를 높입니다.
- (r1, c1)부터 (r2, c2)까지 직사각형 모양의 범위 안에 있는 건물의 내구도를 degree 만큼 낮추거나 높인다는 뜻입니다.
- 0 ≤ r1 ≤ r2 <
board
의 행의 길이 - 0 ≤ c1 ≤ c2 <
board
의 열의 길이 - 1 ≤ degree ≤ 500
- type이 1이면 degree만큼 건물의 내구도를 낮춥니다.
- type이 2이면 degree만큼 건물의 내구도를 높입니다.
- 0 ≤ r1 ≤ r2 <
- type은 1 혹은 2입니다.
- 건물은 파괴되었다가 회복 스킬을 받아 내구도가 1이상이 되면 파괴되지 않은 상태가 됩니다. 즉, 최종적으로 건물의 내구도가 1이상이면 파괴되지 않은 건물입니다.
정확성 테스트 케이스 제한 사항
- 1 ≤
board
의 행의 길이 (=N
) ≤ 100 - 1 ≤
board
의 열의 길이 (=M
) ≤ 100 - 1 ≤
board
의 원소 (각 건물의 내구도) ≤ 100 - 1 ≤
skill
의 행의 길이 ≤ 100- 1 ≤ degree ≤ 100
효율성 테스트 케이스 제한 사항
- 주어진 조건 외 추가 제한사항 없습니다.
# 접근 방법
- 주어진 문제를 정확성 테스트만 통과하기 위해서는 브루트 포스로 풀어주면 된다.
- board의 크기가 100 x 100 이하이고
- skill의 개수가 100개 이하이기 때문에
def solution(board, skill):
answer = 0
N, M = len(board), len(board[0])
for t, si, sj, ni, nj, degree in skill:
if t == 1:
for i in range(si, ni+1):
for j in range(sj, nj+1):
board[i][j] -= degree
else:
for i in range(si, ni+1):
for j in range(sj, nj+1):
board[i][j] += degree
for i in range(N):
for j in range(M):
if board[i][j] > 0:
answer += 1
return answer
- 다만 효율성 테스트도 있기 때문에 다른 풀이 방법이 필요하다.
- skill이 주어지면서 내구도를 감소시키고 회복시키는 일이 반복 발생하므로 누적합을 생각해주었다.
- 주어지는 좌표의 [시작 행, 시작열], [시작 행, 마지막 열+1] 부터 마지막 행까지 주어지는 degree 값을 반대로 입력해주면 된다.
- 이를 위해서 N+1만큼의 행, M+1만큼의 열을 가진 prefix 배열을 생성해준다.
- 0, 0에서 3, 4까지 4씩 감소 시킨다면
- [[-4, 0, 0, 0, 0, 4], [-4, 0, 0, 0, 0, 4], [-4, 0, 0, 0, 0, 4], [-4, 0, 0, 0, 0, 4]] 배열이 생성되고 이를 한 행씩 누적합 시킨다면
- [[-4, -4, -4, -4, -4, 0], [-4, -4, -4, -4, -4, 0], [-4, -4, -4, -4, -4, 0], [-4, -4, -4, -4, -4, 0]] 가 되어 각 좌표마다 더해주거나 빼주는 값이 계산된다.
prefix = [[0] * (M+1) for _ in range(N)]
for t, si, sj, ni, nj, degree in skill:
val = degree if t == 2 else -degree
for i in range(si, ni+1):
prefix[i][sj] += val
prefix[i][nj+1] -= val
for i in range(N):
for j in range(1, M):
prefix[i][j] += prefix[i][j-1]
- 위의 경우도 효율성 테스트는 통과하지 못하였다..
- skill의 길이가 250000가 되므로 최악의 경우 행의 최대 개수 1000 * 250000이 되어 시간 초과가 발생한다.
- 따라서, 행의 경우도 반복문을 통해 하나하나 기록해주는 것이 아닌 시작 행과 마지막 행 +1 에 기록해준 후
- prefix 합을 가로 뿐만 아니라 세로로도 구해주면 된다.
def solution(board, skill):
answer = 0
N, M = len(board), len(board[0])
prefix = [[0] * (M+1) for _ in range(N+1)]
for t, si, sj, ni, nj, degree in skill:
val = degree if t == 2 else -degree
prefix[si][sj] += val
prefix[si][nj+1] -= val
prefix[ni+1][sj] -= val
prefix[ni+1][nj+1] += val
for i in range(N):
for j in range(M):
prefix[i][j+1] += prefix[i][j]
for j in range(M):
for i in range(N):
prefix[i+1][j] += prefix[i][j]
for i in range(N):
for j in range(M):
if board[i][j] + prefix[i][j] > 0:
answer += 1
return answer
728x90
'ALGORITHM > 정렬, 탐색,구현' 카테고리의 다른 글
[백준 16916번] 파이썬 - 부분 문자열 (0) | 2023.08.07 |
---|---|
[백준 25907번] 파이썬 - 양과 늑대 (0) | 2023.08.04 |
[백준 1182번] 파이썬 - 부분 수열의 합 (0) | 2023.07.24 |
[백준 18500번] 파이썬 - 미네랄 2 (0) | 2023.07.19 |
[프로그래머스 Lv3] 파이썬 - 표 병합 (0) | 2023.07.16 |