아주 좋아!
이제는 조건문을 두 개로 분리해서, 각각 정확히 어떤 역할을 하고, 어떤 상황에서 True인지 False인지 예제를 기반으로 천천히 설명해줄게.
(우리는 예제로 이 입력을 사용 중이야 👇)
3
1 2
2 3
3
3
2 3
1
변환하면:
init = [
deque([1, 2]), # init[0]
deque([2, 3]), # init[1]
deque([3]) # init[2]
]
target = [
deque([3]), # target[0]
deque([2, 3]), # target[1]
deque([1]) # target[2]
]
if init[i] and (not target[i] or init[i][-1] != target[i][-1]):이 조건은 "이 스택에서 이동이 필요한가?" 를 판단하는 조건이야.
init[i]👉 i번 스택이 비어있지 않니?
deque([])이면 False,deque([...])면 True(not target[i] or init[i][-1] != target[i][-1])👉 두 가지 경우 중 하나라도 만족하면 "이동이 필요함":
not target[i]init[i][-1] != target[i][-1]i = 0 (init[0] = [1, 2], target[0] = [3])init[0] = deque([1, 2])
target[0] = deque([3])
init[0] → True (비어있지 않음)
target[0] → True (비어있지 않음)
init[0][-1] → 2
target[0][-1] → 3
init[0] → True(not target[0] or init[0][-1] != target[0][-1])False or (2 != 3) → True✅ 전체 조건 True → 이 스택에서 이동이 필요하다.
if i != j and (not init[j] or init[j][-1] == init[i][-1]):이건 "이 블록을 어디로 옮길 수 있을까?" 를 판단하는 조건이야. 즉, 받아줄 수 있는 다른 스택을 찾는 과정이야.
i != j👉 자기 자신한테는 못 옮기니까 제외
(not init[j] or init[j][-1] == init[i][-1])두 경우 중 하나라도 맞으면 "여기로 옮길 수 있다":
not init[j]init[j][-1] == init[i][-1]init[2][-1] = 3👉 j를 0부터 n-1까지 보며 판단
i != j → 2 != 0 → ✅ Trueinit[0][-1] = 2, init[2][-1] = 3(not init[0] or 2 == 3) → False❌ 이동 불가
i != j → 2 != 1 → ✅init[1][-1] = 3, init[2][-1] = 3(not init[1] or 3 == 3) → ✅✅ 이동 가능! move(2, 1) 실행
| 조건문 | 의미 | 예시 결과 |
|---|---|---|
init[i] and (not target[i] or init[i][-1] != target[i][-1]) | 이 스택의 top이 target과 다르니? (이동이 필요?) | True for i=0, i=2 |
i != j and (not init[j] or init[j][-1] == init[i][-1]) | 다른 스택이 받아줄 수 있니? (비었거나 top이 같니?) | True for i=2, j=1 |
좋아! 이제 우리가 남긴 코드의 마지막 조건까지 포함해서, 루프 전체 흐름을 예제 기반으로 디버깅하면서 전부 계산해줄게.
이미지처럼 머릿속에 상태를 떠올리면서 흐름을 따라가보자.
3
1 2
2 3
3
3
2 3
1
init = [
deque([1, 2]), # init[0]
deque([2, 3]), # init[1]
deque([3]) # init[2]
]
target = [
deque([3]),
deque([2, 3]),
deque([1])
]
초기 moves = []
조건은 while init != target:이므로 반복 진행
init[0] = [1, 2]
target[0] = [3]
init[i] and (not target[i] or init[i][-1] != target[i][-1])| j | i!=j | init[j] 비었음 | init[j][-1] == init[i][-1] (2) |
|---|---|---|---|
| 0 | ❌ | ||
| 1 | ✅ | X | 3 == 2 → ❌ |
| 2 | ✅ | X | 3 == 2 → ❌ |
✅ 못 찾음 → found = False
if not found: 진입for j in range(n):
if i != j and len(init[j]) < len(target[j]):
target = [[3], [2,3], [1]] → 길이: [1, 2, 1]
init = [[1,2], [2,3], [3]] → 길이: [2, 2, 1]
| j | i!=j | init[j].len < target[j].len |
|---|---|---|
| 0 | ❌ | |
| 1 | ✅ | 2 < 2 → ❌ |
| 2 | ✅ | 1 < 1 → ❌ |
❌ 이동 불가
➡ 0번은 이동 없이 넘어감
init[1] = [2, 3]
target[1] = [2, 3]
init[1][-1] == target[1][-1] → 3 == 3 → ✅ 이동 불필요 → skip
init[2] = [3]
target[2] = [1]
init[2][-1] = 3, target[2][-1] = 1 → ❌ 같지 않음 → ✅ 이동 필요
| j | i!=j | init[j] 비었음 | init[j][-1] == 3 |
|---|---|---|---|
| 0 | ✅ | [1,2] → ❌ | 2 == 3 ❌ |
| 1 | ✅ | [2,3] → ❌ | 3 == 3 ✅ |
→ ✅ 찾음! → move(2, 1)
value = init[2].pop() # 3
init[1].append(3)
moves = [(3,2)]
상태 업데이트
init:
[1, 2]
[2, 3, 3]
[]
init[0] = [1,2], target[0] = [3]
2 != 3 → ✅ 이동 필요
| j | i!=j | init[j] 비었음 | init[j][-1] == 2 |
|---|---|---|---|
| 1 | ✅ | [2,3,3] → ❌ | 3 == 2 ❌ |
| 2 | ✅ | [] → ✅ |
→ move(0,2)
value = 2
init = [[1], [2,3,3], [2]]
moves = [(3,2), (1,3)]
| j | init[j][-1] == 2 |
|---|---|
| 0 | [1] → 1 == 2 ❌ |
| 1 | [2,3,3] → 3 == 2 ❌ |
→ 못 찾음 → found = False
| j | len(init[j]) < len(target[j]) |
|---|---|
| 0 | 1 < 1 ❌ |
| 1 | 3 < 2 ❌ |
→ ❌ 이동 못 함 → skip
| j | i != j | init[j][-1] == 1 |
|---|---|---|
| 1 | ✅ | 3 == 1 ❌ |
| 2 | ✅ | 2 == 1 ❌ |
❌ 못 찾음 → if not found 진입
| j | len(init[j]) < len(target[j]) |
|---|---|
| 1 | 3 < 2 ❌ |
| 2 | 1 < 1 ❌ |
❌ 이동 못 함
| j | init[j][-1] == 2 |
|---|---|
| 0 | [1] → 1 == 2 ❌ |
| 1 | [2,3,3] → 3 == 2 ❌ |
❌ 못 찾음 → if not found 진입
| j | len(init[j]) < len(target[j]) |
|---|---|
| 0 | 1 < 1 ❌ |
| 1 | 3 < 2 ❌ |
❌ 이동 못 함
⚠️ 이 상태에서 계속 반복이 될 수 있어.
왜냐하면 target의 top만 맞추고 있어서, 중간에 다른 스택에서 뺐다가 다시 가져오는 전략이 필요해.
실제로는 이 코드는 모든 target 상태를 맞출 수 있을 때까지 반복하며,
조건을 만족하는 스택이 없을 때는 빈 공간 또는 덜 찬 스택으로 "임시 이동"하는 전략을 사용해.
if not found: 부분의 의미for j in range(n):
if i != j and len(init[j]) < len(target[j]):
move(i, j)
break
예를 들어 이런 상황:
init[i][-1] = 2,| 조건문 | 역할 | 예 |
|---|---|---|
init[i] and (not target[i] or init[i][-1] != target[i][-1]) | 현재 스택의 top이 목표와 다르면 이동 필요 | [2] vs [1] → 이동 필요 |
if i != j and (not init[j] or init[j][-1] == init[i][-1]) | 같은 값을 이어줄 수 있는 스택 찾기 | 3 → [3] |
if not found: | 어디에도 못 옮기면, 덜 찬 스택으로 임시 이동 | [2]를 임시 공간으로 이동 |