하노이 탑 K
1 초 | 1024 MB | 758 | 199 | 133 | 34.635% |
문제
세 개의 장대가 있고 첫 번째 장대에는 반경이 서로 다른 n개의 원판이 쌓여 있다. 각 원판은 반경이 큰 순서대로 쌓여있다. 이제 수도승들이 다음 규칙에 따라 첫 번째 장대에서 세 번째 장대로 옮기려 한다.
- 한 번에 한 개의 원판만을 다른 탑으로 옮길 수 있다.
- 쌓아 놓은 원판은 항상 위의 것이 아래의 것보다 작아야 한다.
이 작업을 수행하는 필요한 이동 순서 중에서 K번째를 출력하는 프로그램을 작성하라. 단, 이동 횟수는 최소가 되어야 한다.
아래 그림은 원판이 5개인 경우의 예시이다.
입력
첫째 줄에 첫 번째 장대에 쌓인 원판의 개수 N(1 ≤ N ≤ 60)과 K가 주어진다. 항상 K번째 이동이 존재하는 K만 주어진다.
출력
첫째 줄에 K번째 수행 과정을 의미하는 두 정수 A B를 빈칸을 사이에 두고 출력한다. 이는 A번째 탑의 가장 위에 있는 원판을 B번째 탑의 가장 위로 옮긴다는 뜻이다.
예제 입력 1 복사
3 1
예제 출력 1 복사
1 3
예제 입력 2 복사
3 2
예제 출력 2 복사
1 2
예제 입력 3 복사
3 3
예제 출력 3 복사
3 2
예제 입력 4 복사
3 6
예제 출력 4 복사
2 3
예제 입력 5 복사
3 7
예제 출력 5 복사
1 3
https://www.acmicpc.net/problem/23250
오랜만에 하노이탑 개념을 봐서 한번 정리했다.
하노이탑 코드
#include <bits/stdc++.h>
using namespace std;
int n;
void Hanoi(int n, int from, int to, int by){
if(n==1){ //종료 조건, 원판 하나 남아있을때는 바로 목적지로 옮김
cout << from << "->" << to <<"\n";
return;
}
else{
Hanoi(n-1, from, by, to); //n-1개를 from->by(보조기둥)
cout << from << "->" << to <<"\n"; //가장 큰 1개는 from->to(목적지)
Hanoi(n-1, by, to, from); //n-1개 by->to(목적지로 옮기기)
}
}
int main(){
ios_base::sync_with_stdio(0); cin.tie(NULL); cout.tie(NULL);
cin >> n;
int num = pow(2,n)-1;
Hanoi(n, 1,3,2); //5개의 원판, 1번 기둥(출발지), 3번 기둥(목적지), 2번 기둥(중간점)
}
11729번: 하노이탑 이동 순서
https://www.acmicpc.net/status?user_id=starlight258&problem_id=11729&from_mine=1
#include <bits/stdc++.h>
using namespace std;
int n;
void Hanoi(int n, int from, int to, int by){
if(n==1){ //종료 조건, 원판 하나 남아있을때는 바로 목적지로 옮김
cout << from << " " << to <<"\n";
return;
}
else{
Hanoi(n-1, from, by, to); //n-1개를 from->by(보조기둥)
cout << from << " " << to <<"\n"; //가장 큰 1개는 from->to(목적지)
Hanoi(n-1, by, to, from); //n-1개 by->to(목적지로 옮기기)
}
}
int main(){
ios_base::sync_with_stdio(0); cin.tie(NULL); cout.tie(NULL);
cin >> n;
int num = pow(2,n)-1;
cout << num<<"\n";
Hanoi(n, 1,3,2); //5개의 원판, 1번 기둥(출발지), 3번 기둥(목적지), 2번 기둥(중간점)
}
다시 23250번 문제로 돌아가보자.
#include <bits/stdc++.h>
using namespace std;
int n,k,cnt;
void Hanoi(int n, int from, int to, int by){
if(n==1){ //종료 조건, 원판 하나 남아있을때는 바로 목적지로 옮김
cnt++; if(cnt==k) cout <<from << " " << to <<"\n";
return;
}
else{
Hanoi(n-1, from, by, to); //n-1개를 from->by(보조기둥)
cnt++;
if(cnt==k){
cout << from << " " << to <<"\n"; //가장 큰 1개는 from->to(목적지)
return;
}
Hanoi(n-1, by, to, from); //n-1개 by->to(목적지로 옮기기)
}
}
int main(){
ios_base::sync_with_stdio(0); cin.tie(NULL); cout.tie(NULL);
cin >> n >> k;
Hanoi(n, 1,3,2); //5개의 원판, 1번 기둥(출발지), 3번 기둥(목적지), 2번 기둥(중간점)
}
k번째 연산을 구하는 문제이므로 간단하게 구현했지만 시간초과가 되었다.
원판 n개의 이동개수는 2^n-1이다.
n은 최대 60이므로 엄청나게 큰 숫자가 된다... 일부분만 실행하거나 재귀로 구현하면 안된다.
어떻게 풀지 모르겠어서 블로그 코드를 참고했다.
https://blog.naver.com/khckmc316/222900584511
블로그 설명- 하노이탑 재귀 경우의 수를 줄인다.
정리한 코드
#include <bits/stdc++.h>
using namespace std;
int n;
long long k;
void Hanoi(int n, long long mid, int from, int to, int by){
if(n==1){
cout << from << " " << to;
return;
}
else if(k<mid){
Hanoi(n-1, (long long)(pow(2,n-1))/2, from,by,to);
}
else if (k>mid){
k -= mid; //1번,2번 수행 횟수를 k에서 빼줌
Hanoi(n-1, (long long)(pow(2,n-1))/2 , by,to,from);
}
else if(k==mid){
cout << from << " " << to;
return;
}
}
int main(){
ios_base::sync_with_stdio(0); cin.tie(NULL); cout.tie(NULL);
//1. 입력받기
cin >> n >> k;
//2. 총 연산개수 구하기
long long total = (long long)(pow(2,n));
//3. 하노이탑 수행 - k 위치에 따라 실행되는 연산 다름
Hanoi(n, total/2, 1,3,2); //1번기둥(출발지), 3번기둥(목적지), 2번기둥(중간)
}
여기서 총 연산개수를 2^n-1이 아닌 2^n이라고 잡은 이유
그림 출처: https://brunch.co.kr/@younggiseo/139
n=3일때 1번,2번,3번 과정에서 1번 과정은 1~4번 수행되기 때문이다.
'알고리즘 > 알고리즘 문제풀이' 카테고리의 다른 글
1781번: 컵라면 (0) | 2023.03.07 |
---|---|
9935번: 문자열 폭발 (0) | 2023.03.06 |
2109번: 순회강연 [C++] (0) | 2023.03.06 |
5주차: 그리디, 라인스위핑, 투포인터 (2) | 2023.03.05 |
1874번: 스택 수열 (0) | 2023.03.05 |