Backtracking

Backtracking κ°œλ…

  • μ—¬λŸ¬ 가지 선택지듀이 μ‘΄μž¬ν•˜λŠ” μƒν™©μ—μ„œ ν•œκ°€μ§€λ₯Ό 선택

  • 선택이 이루어지면 μƒˆλ‘œμš΄ μ„ νƒμ§€λ“€μ˜ 집합이 생성됨

  • 이런 선택을 λ°˜λ³΅ν•˜λ©΄μ„œ μ΅œμ’… μƒνƒœμ— 도달

    • μ˜¬λ°”λ₯Έ 선택을 κ³„μ†ν•˜λ©΄ λͺ©ν‘œ μƒνƒœ(goal state)에 λ„λ‹¬ν•œλ‹€!

Backtracking과 DFS의 차이

  • μ–΄λ–€ nodeμ—μ„œ μΆœλ°œν•˜λŠ” κ²½λ‘œκ°€ ν•΄κ²°μ±…μœΌλ‘œ μ΄μ–΄μ§ˆ 것 같지 μ•ŠμœΌλ©΄ 더 이상 κ·Έ 경둜λ₯Ό 따라가지 μ•ŠμŒμœΌλ‘œμ¨ μ‹œλ„μ˜ 횟수λ₯Ό μ€„μž„

    • Prunning (κ°€μ§€μΉ˜κΈ°)

  • DFSκ°€ λͺ¨λ“  경둜λ₯Ό μΆ”μ ν•˜λŠ”λ° λΉ„ν•΄ Backtacking은 λΆˆν•„μš”ν•œ 경둜λ₯Ό 쑰기에 차단함

  • DFSλ₯Ό ν•˜κΈ°μ—λŠ” 경우의 μˆ˜κ°€ λ„ˆλ¬΄ λ§Žμ„ λ•Œ

    • N! κ°€μ§€μ˜ 경우의 수λ₯Ό 가진 λ¬Έμ œμ— λŒ€ν•΄ DFSλ₯Ό ν•˜λ©΄ 처리 λΆˆκ°€λŠ₯ν•˜λ‹€

  • Back Tracking μ•Œκ³ λ¦¬μ¦˜μ„ μ μš©ν•˜λ©΄ 일반적으둜 경우의 μˆ˜κ°€ μ€„μ–΄λ“€μ§€λ§Œ, 이 μ—­μ‹œ μ΅œμ•…μ˜ κ²½μš°μ—λŠ” μ—¬μ „νžˆ μ§€μˆ˜ν•¨μˆ˜ μ‹œκ°„ (Exponential Time)을 μš”ν•˜λ―€λ‘œ 처리 λΆˆκ°€λŠ₯

  • Root nodeμ—μ„œ Leaf node κΉŒμ§€μ˜ κ²½λ‘œλŠ” ν•΄λ‹΅ 후보 (candidate solution)κ°€ λ˜λŠ”λ°, DFSλ₯Ό ν•˜μ—¬ κ·Έ ν•΄λ‹΅ 후보 μ€‘μ—μ„œ 해닡을 찾을 수 있음

    • but, 이 방법을 μ‚¬μš©ν•˜λ©΄ 해닡이 될 κ°€λŠ₯성이 μ „ν˜€ μ—†λŠ” node의 μžμ‹ node (descendant) 듀도 λͺ¨λ‘ 검색해야 ν•˜λ―€λ‘œ λΉ„νš¨μœ¨μ 

Backtracking 기법

  • μ–΄λ–€ node의 μœ λ§μ„±μ„ μ κ²€ν•œ 후에 유망(promising) ν•˜μ§€ μ•Šλ‹€κ³  κ²°μ •λ˜λ©΄ κ·Έ node의 λΆ€λͺ¨λ‘œ λ˜λŒμ•„κ°€μ„œ (backtracking) λ‹€μŒ μžμ‹ node둜 감

  • μ–΄λ–€ nodeλ₯Ό λ°©λ¬Έν•˜μ˜€μ„ λ•Œ

    • κ·Έ nodeλ₯Ό ν¬ν•¨ν•œ κ²½λ‘œκ°€ 해닡이 될 수 μ—†μœΌλ©΄ κ·Έ nodeλŠ” μœ λ§ν•˜μ§€ μ•Šλ‹€κ³  함

    • 해닡이 κ°€λŠ₯성이 있으λͺ… μœ λ§ν•˜λ‹€κ³  함

κ°€μ§€μΉ˜κΈ° (prunning)

: μœ λ§ν•˜μ§€ μ•Šμ€ nodeκ°€ ν¬ν•¨λœ κ²½λ‘œλŠ” 더 이상 κ³ λ €ν•˜μ§€ μ•ŠλŠ”λ‹€

Backtracking을 μ΄μš©ν•œ μ•Œκ³ λ¦¬μ¦˜μ˜ 절차

  1. μƒνƒœ 곡간 트리의 DFS (깊이 μš°μ„  검색)을 μ‹€μ‹œν•œλ‹€

  2. 각 nodeκ°€ μœ λ§ν•œμ§€λ₯Ό μ κ²€ν•œλ‹€

  3. 만일 κ·Έ nodeκ°€ μœ λ§ν•˜μ§€ μ•ŠμœΌλ©΄, κ·Έ node의 λΆ€λͺ¨ node둜 λŒμ•„κ°€μ„œ 검색을 κ³„μ†ν•œλ‹€

N-Queen 문제

SWEA 2806

def backtrack(idx): #idx = ν–‰
    global N, cnt
    # μ΅œμ’… μƒνƒœμΈμ§€ ν™•μΈν•˜κ³ , μ΅œμ’…μƒνƒœμ΄λ©΄ ν•΄
    if idx == N:
        # λ‹€ μ°Ύμ•˜μŒ - ν•΄!
        cnt +=1
        return 
    # ν•΄λ‹Ή μƒνƒœμ—μ„œ 선택할 수 μžˆλŠ” 후보ꡰ 생성
    # Nodeκ°€ μœ λ§ν•œμ§€ 확인 : μ—΄ / 상ν–₯ λŒ€κ° / ν•˜ν–₯ λŒ€κ° 확인
    for i in range(N):
        # if 열이 μœ λ§ν•˜κ³ , λŒ€κ°λ“€μ΄ 유망
        if not col[i] and not dia_1[idx +i] and not dia_2[N+i-idx-1]:
            # λͺ¨λ“  후보ꡰ에 λŒ€ν•΄μ„œ λ‹€μŒ μƒνƒœ μ‹€ν–‰ 
            col[i] = 1
            dia_1[idx+i] = 1
            dia_2[N+i-idx-1] = 1
            # 행을 μ¦κ°€μ‹œν‚€κΈ°
            backtrack(idx+1)
            # λ‹€μ‹œ μ‚¬μš©ν•œ 것을 μ—†μ• μ€Œ
            col[i] = 0
            dia_1[idx+i] = 0
            dia_2[N+i-idx-1] = 0

T = int(input())
for t in range(1, T+1):
    N = int(input())

    # 각 ν–‰μ—λŠ” 1개의 Queen만 올 수 있음
    col = [0] * N # μ—΄μ˜ μ‚¬μš© μ—¬λΆ€λ₯Ό νŒλ‹¨ν•˜λŠ” 리슀트
    # λŒ€κ° μœ λ§μ„±μ„ νŒλ‹¨ν•  리슀트
    
    # 상ν–₯ λŒ€κ°: i와 j의 합이 κ°™μŒ
    dia_1 = [0] * (2*N -1) # (r+c) 둜 λŒ€κ°μ„  번호 맀김
    # ν•˜ν–₯ λŒ€κ°: i-j μ°¨κ°€ 일정
    dia_2 = [0] * (2*N -1) # (N + c -r -1) 둜 λŒ€κ°μ„  번호 맀김

    cnt = 0
    backtrack(0)
    print('#{} {}'.format(t,cnt))

Subset

뢀뢄집합 κ΅¬ν•˜κΈ°

ex)

# {1,2,3,4,5,6,7,8,9,10} 의 powerset 쀑 μ›μ†Œγ…‘μ΄ 값이 10인 뢀뢄집합을 λͺ¨λ‘ 좜λ ₯ν•˜μ‹œμ˜€.

def backtrack(arr, idx, N, selected, sum_num):
    if sum_num > 10:
        return
    if idx == N: 
        # 총합이 10이면 좜λ ₯
        if sum_num == 10:
            for i in range(N):
                if selected[i]:
                    print(arr[i], end=' ')
            print()
        return

    #ν˜„μž¬ indexλ₯Ό 선택 ν–ˆμ„ 경우
    selected[idx] =1
    backtrack(arr, idx+1, N, selected, sum_num + arr[idx])

    #ν˜„μž¬ index λ₯Ό μ„ νƒν•˜μ§€ μ•Šμ•˜μ„ 경우
    selected[idx] = 0
    backtrack(arr, idx+1, N, selected, sum_num )


arr = [1,2,3,4,5,6,7,8,9,10]

backtrack(arr, 0, 10, [0]*10, 0)

Space State Treeλ₯Ό κ΅¬μΆ•ν•˜μ—¬ 문제 ν•΄κ²°ν•˜κΈ°

Backtracking을 μ΄μš©ν•˜μ—¬ μˆœμ—΄ κ΅¬ν•˜κΈ°

μ ‘κ·Ό 방법은 뢀뢄집합 κ΅¬ν•˜λŠ” 방법과 μœ μ‚¬

ex)

def backtrack(result, selected, idx, N):
    if idx == N:
        print(result)
        return
    # μ‚¬μš© κ°€λŠ₯ν•œ 선택지 후보ꡰ에 λŒ€ν•˜μ—¬ λ‹€μŒ λ‹¨κ³„λ‘œ 진행
    for i in range(N):
        if not selected[i]:
            selected[i] = 1
            result[idx] = i
            backtrack(result, selected, idx+1, N)
            selected[i] = 0

N = 5
backtrack([0]*N, [0]*N, 0, N)

Last updated