삼성 시뮬레이션, 왜 맨날 한 끗 차이로 틀릴까

알고리즘은 아는데 삼성 시뮬레이션만 만나면 자꾸 틀린다. 8/10 맞고 나머지 2개에서 막힌다. 이거 알고리즘 실력 문제가 아니다. 코드 구조 문제다.

삼성 SW 역량테스트 기출을 수십 개 분석해보면 패턴이 보인다. 시뮬레이션 문제가 요구하는 건 BFS/DFS 같은 고급 알고리즘이 아니라, 복잡한 조건을 빠짐없이 처리하는 구현 정확도다. 그런데 대부분 이걸 "꼼꼼함"의 문제로만 생각한다. 틀렸다. 구조가 엉망이면 아무리 꼼꼼해도 놓친다.

삼성 시뮬레이션의 정체

삼성 문제를 한 줄로 요약하면: 2차원 격자 위에서 여러 객체가 규칙에 따라 상태를 바꾸는 과정을 정확히 구현하라.

문제 자체는 어렵지 않다. 조건이 많을 뿐이다. 그리고 그 조건들이 서로 얽혀 있어서, 처리 순서를 잘못 잡으면 연쇄적으로 틀어진다.

전형적인 함정들:

  • 이동과 충돌을 동시에 처리해야 하는데 순차적으로 처리함

  • 배열을 원본 그대로 읽어야 하는데 업데이트된 배열을 읽음

  • 방향 전환 조건에서 경계값을 빠뜨림

구조화 전략: 3단계로 나눠라

시뮬레이션 코드를 짤 때 한 함수에 전부 때려넣으면 망한다. 무조건 단계를 나눠야 한다.

def simulate():
    for _ in range(T):
        # 1단계: 상태 읽기 (현재 프레임 스냅샷)
        snapshot = copy_state(board)
        
        # 2단계: 규칙 적용 (스냅샷 기준으로 판단)
        actions = decide_actions(snapshot)
        
        # 3단계: 상태 반영 (한꺼번에 업데이트)
        apply_actions(board, actions)

핵심은 읽기와 쓰기를 분리하는 것이다. 매 턴마다 현재 상태의 스냅샷을 찍고, 그 스냅샷을 기준으로 모든 판단을 내리고, 판단이 끝난 뒤에 한꺼번에 반영한다.

이게 왜 중요하냐면, 삼성 문제의 절반 이상이 "동시 처리"를 요구하기 때문이다. 객체 A가 이동한 결과가 객체 B의 이동에 영향을 주면 안 되는 경우가 대부분이다.

방향 배열, 제발 통일해라

삼성 문제에서 가장 많이 실수하는 부분. 상하좌우 방향을 매번 다르게 정의하면 반드시 한 번은 헷갈린다.

# 이거 한 번 정하면 절대 바꾸지 마라
# 상 우 하 좌 (시계방향)
dy = [-1, 0, 1, 0]
dx = [0, 1, 0, -1]

def turn_right(d):
    return (d + 1) % 4

def turn_left(d):
    return (d + 3) % 4

def turn_back(d):
    return (d + 2) % 4

문제마다 방향 순서가 다르게 주어질 수 있다. 그때 방향 배열 자체를 바꾸는 게 아니라, 입력을 내 방향 체계로 매핑하는 함수를 하나 만들어라. 이것만으로도 방향 관련 버그가 확 줄어든다.

deepcopy 대신 수동 복사

copy.deepcopy는 느리다. 삼성 문제에서 시간제한에 걸리는 이유 중 하나다. 2차원 배열 복사는 이렇게 하자:

# 이게 deepcopy보다 5~10배 빠름
new_board = [row[:] for row in board]

객체 리스트를 복사해야 한다면 튜플을 써라. 튜플은 불변이라 복사 비용이 거의 없다.

# 각 객체를 (y, x, dir, state) 튜플로 관리
entities = [(2, 3, 0, 'alive'), (5, 1, 2, 'alive')]
snapshot = entities[:]  # 튜플이라 얕은 복사로 충분

디버깅: print 대신 격자 시각화

테스트케이스 2~3개가 안 맞을 때, print문을 여기저기 찍는 건 비효율적이다. 격자 상태를 한눈에 보는 함수를 미리 만들어둬라.

def show(board, entities):
    grid = [['.' for _ in range(N)] for _ in range(N)]
    for i, (y, x, d, s) in enumerate(entities):
        if s == 'alive':
            grid[y][x] = str(i)
    for row in grid:
        print(' '.join(row))
    print('---')

매 턴마다 이걸 호출하면 어느 시점에서 상태가 틀어지는지 바로 보인다. 삼성 문제는 로직 에러보다 특정 턴에서 조건 하나 빠뜨린 것이 원인인 경우가 많아서, 시각화가 디버깅 시간을 절반으로 줄여준다.


정리하면: 삼성 시뮬레이션은 알고리즘 시험이 아니라 소프트웨어 설계 시험에 가깝다. 읽기/쓰기 분리, 방향 체계 통일, 빠른 복사, 시각화 디버깅. 이 네 가지를 습관으로 만들면 "8/10에서 막히는" 현상은 사라진다.