[Algorithm] 하노이 탑 보충 정리

6720·2023년 4월 29일
0

이거 모르겠어요

목록 보기
16/38
post-custom-banner

다음에서 이어짐

문제(프로그래머스 Lv. 2 하노이의 탑)

문제 설명

하노이 탑(Tower of Hanoi)은 퍼즐의 일종입니다. 세 개의 기둥과 이 기동에 꽂을 수 있는 크기가 다양한 원판들이 있고, 퍼즐을 시작하기 전에는 한 기둥에 원판들이 작은 것이 위에 있도록 순서대로 쌓여 있습니다. 게임의 목적은 다음 두 가지 조건을 만족시키면서, 한 기둥에 꽂힌 원판들을 그 순서 그대로 다른 기둥으로 옮겨서 다시 쌓는 것입니다.

  1. 한 번에 하나의 원판만 옮길 수 있습니다.
  2. 큰 원판이 작은 원판 위에 있어서는 안됩니다.

하노이 탑의 세 개의 기둥을 왼쪽 부터 1번, 2번, 3번이라고 하겠습니다. 1번에는 n개의 원판이 있고 이 n개의 원판을 3번 원판으로 최소 횟수로 옮기려고 합니다.

1번 기둥에 있는 원판의 개수 n이 매개변수로 주어질 때, n개의 원판을 3번 원판으로 최소로 옮기는 방법을 return하는 solution를 완성해주세요.

제한사항

  • n은 15이하의 자연수 입니다.

입출력 예

nresult
2[[1,2],[1,3],[2,3]]

불과 몇 개월 전에는 알고리즘 조차 몰랐는데 지금 다시 보니 풀이 방식과 재귀 형태가 DFS와 유사하여 다시 풀게 됨.

하노이 탑 원리

여기에서 설명 했지만 다시 설명하자면,

하노이 탑의 기둥이 3개 있다고 가정할 때, 첫 번째 기둥에 쌓여있던 블록을 전부 원 상태로 세 번째 기둥으로 옮기는 것.
단, 블록은 자신보다 더 큰 블록을 위에 올릴 수 없다는 제한이 있음.

결론적으로 하노이 탑은 한꺼번에 해결할 수 없으며, 가장 크기가 큰, 맨 밑에 있던 블록부터 차례대로 세 번째 기둥에 쌓아야 함.

그러기 위해선 다음을 수행해야 함.

  • 맨 밑에 있던 블록을 n 번째 블록이라고 하면, n-1개의 블록을 다른 기둥으로 옮겨둘 필요가 있음.
  • 그런 후 마지막에 n 번째 블록을 세 번째 기둥으로 옮겨야 함.
  • n-1개의 블록도 이처럼 반복할 것.

백준 11729 하노이 탑 이동 순서와 다른 점

백준에서 풀었던 하노이 탑 문제는 바로 출력해주는 문제이지만, 이번 문제는 이를 배열에 담아서 출력해야 하므로 구조적으로 수정할 필요가 있음.

<전에 작성했던 hanoi 함수>

static void hanoi(int n, int from, int to) throws IOException {
	if (n == 1) {
    	bw.write(from + " " + to + "\n");
        return;
    }

    int empty = 6 - from - to;
    hanoi(n-1, from, empty);
    bw.write(from + " " + to + "\n");
    hanoi(n-1, empty, to);
}

문제의 편리함을 위해 배열이 아닌 리스트를 사용해서 구할 예정임.
배열의 크기가 2n12^{n}-1로 정해지긴 했는데 idx 변수를 새로 만들어서 카운팅하도록 했더니 런타임 에러가 떴음.

public void hanoi(int n, int from, int to) {
	if (n == 1) {
		list.add(new int[] {from, to});
		return;
	}
        
	int empty = 6 - from - to;
	hanoi(n-1, from, empty);
	list.add(new int[] {from, to});
	hanoi(n-1, empty, to);
}

DFS와 하노이 탑

1) depth

코드를 봤을 때 값을 바꿔가면서 재귀를 하는 점, 재귀를 1이라는 depth까지 진행한다는 점에서 DFS와 유사함.

여기서의 depth는 이동해야 할 블록의 개수를 칭함. 더 이해 쉽게 설명한다면 블록에 번호가 1번부터 n번까지 가벼운 순으로 적혀있으며, hanoi에서의 n은 n번 블록을 from 기둥에서 to 기둥까지 옮기는 과정을 뜻함.

2) 재귀 형식

hanoi(n-1)이 두 번 실행된다고 설명했는데 이는 위의 예시처럼 두 형제 노드가 생기는 형태와 같아짐.

여기에 형제 노드 첫 번째를 실행(출력)하고 난 후에 부모 노드도 실행(출력)하는 규칙이 있음.
이는 중위 순회와 비슷한 형태를 띄며, DFS에서도 다음과 같은 형태를 많이 사용함.

하노이에서의 중위 순회 형식은 첫 번째 형제 노드는 부모 노드를 옮기기 위한 사전 작업이라고 생각하면 됨. 더 자세히 풀면, n번 블록을 옮기기 위해 그 위에 쌓인 n-1개의 블록을 옮긴다고 생각하면 됨.

-> 4번 블록을 옮기기 위해 첫 번째 형제 노드에서 실행된 결과는 위에 쌓인 3개의 블록을 두 번째 기둥에 옮기는 작업이었음.
그랬기 때문에 4번 블록을 한 번 옮기고 나서는 옮길 일이 없던 것.
이 때문에 첫 번째 형제 노드를 실행하면 무조건 빈 기둥이 존재했던 것도 이 때문임.

참고 자료

https://ksb-dev.tistory.com/212

profile
뭐라도 하자
post-custom-banner

0개의 댓글