티스토리 뷰

반응형

기존 코드

#16933
import sys
from collections import deque

n, m, break_cnt = map(int, input().split())
graph = [list(map(int, input())) for _ in range(n)]
visited = [[[False] * (break_cnt + 1) for _ in range(m)] for _ in range(n)]

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]


def bfs():
    queue = deque()
    queue.append((0, 0, 0, 1, 1))  # 시작 위치, 벽을 부순 횟수, 이동 거리 추가,밤낮여부(낮:1,밤:0)
    visited[0][0][0] = 1
    
    while queue:
        x, y, cur_break_cnt, distance, is_day = queue.popleft()

        # 도착 지점에 도달했을 경우
        if x == (n - 1) and y == (m - 1):
            return distance

        # 인접한 네 방향 탐색
        for i in range(4):
            nx = dx[i] + x
            ny = dy[i] + y

            # 범위 내에 있을 경우
            if 0 <= nx < n and 0 <= ny < m:
                # 벽이 있고, 벽을 부술 수 있으며, 방문하지 않은 경우
                if graph[nx][ny] == 1 and cur_break_cnt < break_cnt and not visited[nx][ny][cur_break_cnt + 1]:
                    if is_day:  # 낮인 경우
                        queue.append((nx, ny, cur_break_cnt + 1, distance + 1, 0))
                        visited[nx][ny][cur_break_cnt + 1] = True
                    else:
                        queue.append((x, y, cur_break_cnt, distance + 1, 1))

                # 벽이 없고 방문하지 않은 경우
                elif graph[nx][ny] == 0 and not visited[nx][ny][cur_break_cnt]:
                    visited[nx][ny][cur_break_cnt] = True
                    if is_day:
                        queue.append((nx, ny, cur_break_cnt, distance + 1, 0))
                    else:
                        queue.append((nx, ny, cur_break_cnt, distance + 1, 1))
    return -1


print(bfs())

 

수정 코드

#16933
import sys
from collections import deque

n, m, break_cnt = map(int, input().split())
graph = [list(map(int, input())) for _ in range(n)]
visited = [[[0] * m for _ in range(n)] for _ in range(break_cnt + 1)]

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]


def bfs():
    queue = deque()
    queue.append((0, 0, 0, 1, 1))  # 시작 위치, 벽을 부순 횟수, 이동 거리 추가,밤낮여부(낮:1,밤:0)
    visited[0][0][0] = 1
    
    while queue:
        x, y, cur_break_cnt, distance, is_day = queue.popleft()

        # 도착 지점에 도달했을 경우
        if x == (n - 1) and y == (m - 1):
            return distance

        # 인접한 네 방향 탐색
        for i in range(4):
            nx = dx[i] + x
            ny = dy[i] + y

            # 범위 내에 있을 경우
            if 0 <= nx < n and 0 <= ny < m:
                # 벽이 있고, 벽을 부술 수 있으며, 방문하지 않은 경우
                if graph[nx][ny] == 1 and cur_break_cnt < break_cnt and not visited[cur_break_cnt + 1][nx][ny]:
                    if is_day:  # 낮인 경우
                        queue.append((nx, ny, cur_break_cnt + 1, distance + 1, 0))
                        visited[cur_break_cnt + 1][nx][ny] = 1
                    else:
                        queue.append((x, y, cur_break_cnt, distance + 1, 1))

                # 벽이 없고 방문하지 않은 경우
                elif graph[nx][ny] == 0 and not visited[cur_break_cnt][nx][ny]:
                    visited[cur_break_cnt][nx][ny] = 1
                    if is_day:
                        queue.append((nx, ny, cur_break_cnt, distance + 1, 0))
                    else:
                        queue.append((nx, ny, cur_break_cnt, distance + 1, 1))
    return -1


print(bfs())

 

 

차이점

: 메모리 접근 패턴이 다름

# 기존코드
visited = [[[False] * (break_cnt + 1) for _ in range(m)] for _ in range(n)]
 if graph[nx][ny] == 1 and cur_break_cnt < break_cnt and not visited[nx][ny][cur_break_cnt + 1]:
    if is_day:  # 낮인 경우
       queue.append((nx, ny, cur_break_cnt + 1, distance + 1, 0))
       visited[nx][ny][cur_break_cnt + 1] = True
       
# 바뀐코드

visited = [[[0] * m for _ in range(n)] for _ in range(break_cnt + 1)]
 if graph[nx][ny] == 1 and cur_break_cnt < break_cnt and not visited[cur_break_cnt + 1][nx][ny]:
    if is_day:  # 낮인 경우
       queue.append((nx, ny, cur_break_cnt + 1, distance + 1, 0))
       visited[cur_break_cnt + 1][nx][ny] = 1

 

 

기존코드 메모리 참조 방식 (불연속적 접근):   visited[0][0][0]에서 visited[0][0][1]로 점프해야함 -> 캐시 활용이 덜 효율적임

visited[0][0][0], visited[0][0][1], visited[0][1][0], visited[0][1][1], visited[0][2][0], visited[0][2][1], visited[1][0][0], visited[1][0][1], visited[1][1][0], visited[1][1][1], visited[1][2][0], visited[1][2][1], visited[2][0][0], visited[2][0][1], visited[2][1][0], visited[2][1][1], visited[2][2][0], visited[2][2][1]

 

바뀐코드 메모리 참조 방식(연속적 접근): cur_break_cnt값에 대해 (x,y)를 순회하며 메모리에 연속 접근함 -> 캐시 효율성을 높일 수 있음

# 벽을 부수지 않고 방문한 경우(0회 부숨): 
visited[0][0][0], visited[0][0][1], visited[0][0][2], visited[0][1][0], visited[0][1][1], visited[0][1][2], visited[0][2][0], visited[0][2][1], visited[0][2][2]
# 벽을 부수고 방문한 경우(1회 부숨): 
visited[1][0][0], visited[1][0][1], visited[1][0][2], visited[1][1][0], visited[1][1][1], visited[1][1][2], visited[1][2][0], visited[1][2][1], visited[1][2][2]

 

연속된 접근 vs. 불연속적인 접근

연속된 접근: 순차적으로 데이터를 읽을 경우 메모리의 연속된 주소를 차례대로 접근함

-> CPU캐시가 이러한 접근 패턴을 예측하고, 미리 다음에 필요할 데이터를 미리 캐시에 로드함

 

불연속적인 접근: 빈번하게 점프하며 데이터에 접근하는 경우 

-> CPU캐시가 예측을 할 수 없어 미리 데이터를 로드하기 어려움. 캐시미스 발생(CPU가 메인메모리에 직접 접근해, 메모리 대기 시간이 증가시킴)

반응형

'Algorithms' 카테고리의 다른 글

StringBuffer 알고리즘 문제 정리  (0) 2020.03.26
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함