문제
네 개의 명령어 D, S, L, R을 이용하는 간단한 계산기가 있다. 이 계산기에는 레지스터가 하나 있는데, 이 레지스터에는 0 이상 10,000 미만의 십진수를 저장할 수 있다. 각 명령어는 이 레지스터에 저장된 n을 다음과 같이 변환한다. n의 네 자릿수를 d1, d2, d3, d4라고 하자(즉 n = ((d1 × 10 + d2) × 10 + d3) × 10 + d4라고 하자)
- D: D는 n을 두 배로 바꾼다. 결과 값이 9999 보다 큰 경우에는 10000으로 나눈 나머지를 취한다. 그 결과 값(2n mod 10000)을 레지스터에 저장한다.
- S: S는 n에서 1을 뺀 결과 n-1을 레지스터에 저장한다. n이 0이라면 9999 가 대신 레지스터에 저장된다.
- L: L 은 n의 각 자릿수를 왼편으로 회전시켜 그 결과를 레지스터에 저장한다. 이 연산이 끝나면 레지스터에 저장된 네 자릿수는 왼편부터 d2, d3, d4, d1이 된다.
- R: R 은 n의 각 자릿수를 오른편으로 회전시켜 그 결과를 레지스터에 저장한다. 이 연산이 끝나면 레지스터에 저장된 네 자릿수는 왼편부터 d4, d1, d2, d3이 된다.
위에서 언급한 것처럼, L과 R 명령어는 십진 자릿수를 가정하고 연산을 수행한다. 예를 들어서 n = 1234 라면 여기에 L을 적용하면 2341 이 되고 R을 적용하면 4123 이 된다.
여러분이 작성할 프로그램은 주어진 서로 다른 두 정수 A와 B(A ≠ B)에 대하여 A를 B로 바꾸는 최소한의 명령어를 생성하는 프로그램이다. 예를 들어서 A = 1234, B = 3412 라면 다음과 같이 두 개의 명령어를 적용하면 A를 B로 변환할 수 있다.
1234 →L 2341 →L 3412
1234 →R 4123 →R 3412
따라서 여러분의 프로그램은 이 경우에 LL이나 RR을 출력해야 한다.
n의 자릿수로 0 이 포함된 경우에 주의해야 한다. 예를 들어서 1000에 L을 적용하면 0001 이 되므로 결과는 1 이 된다. 그러나 R을 적용하면 0100 이 되므로 결과는 100 이 된다.
입력
프로그램 입력은 T 개의 테스트 케이스로 구성된다. 테스트 케이스 개수 T는 입력의 첫 줄에 주어진다. 각 테스트 케이스로는 두 개의 정수 A와 B(A ≠ B)가 공백으로 분리되어 차례로 주어지는데 A는 레지스터의 초기 값을 나타내고 B는 최종 값을 나타낸다. A와 B는 모두 0 이상 10,000 미만이다.
출력
A에서 B로 변환하기 위해 필요한 최소한의 명령어 나열을 출력한다. 가능한 명령어 나열이 여러 가지면, 아무거나 출력한다.
접근 방법
- 하나의 결과를 도출하기 위해서 최소한의 연산을 구하는 점
- A를 B로 변환하는 과정 속에서 명확한 로직을 찾지 못한 점
을 토대로 너비 우선 탐색(BFS) 방식을 떠올렸다.
현재까지 진행된 명령어를 보존해야 했기 때문에 queue에 현재 숫자와 현재 명령어를 pair로 묶어서 저장했다.
주의할 점
위의 접근 방법으로만 문제를 풀면 당연히 메모리 초과, 시간 초과가 발생한다. - (대부분 3%에서 틀린다)
<이를 해결하기 위해서 추가한 방법들>
1. check [10000] 배열을 만들어서 이미 방문했던 숫자는 push 하지 않는다.
2. 제일 마지막으로 'L' 연산을 했다면 'R' 연산을 하지 않고, 제일 마지막으로 'R' 연산을 했다면 'L' 연산을 하지 않았다. 제자리로 돌아오는 행위이기 때문이다.
<개인적으로 실수했던 부분들>1. 한번 bfs함수를 돌릴 때마다, check 배열을 초기화해주지 않았다.
2. 'D'에서는 결괏값이 9999보다 클 때가 예외이지만, 'S'에서는 n이 0일 때가 예외이다.--> n - 1 == 0 일 때가 예외라고 생각했었다. (생각보다 많은 분들이 이 실수하신 것 같다)
※참고로 3번째 테스트 케이스 "LSSD" 나와도 괜찮다.
코드
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
int a, b;
int check[10000] = {0};
void bfs(int start){
queue<pair<int, string> > q;
q.push({start, ""});
check[start] = 1;
while(!q.empty()){
int now = q.front().first;
string cmd = q.front().second;
if(now == b){
cout << cmd << "\n";
return ;
}
q.pop();
// L
if(cmd[cmd.length() - 1] != 'R'){
if(now == 1000){
if(check[1] == 0){
q.push({1, cmd +'L'});
check[1] = 1;
}
}
else{
int tmp_l = now % 1000 * 10 + now / 1000;
string cmd_l = cmd + 'L';
if(check[tmp_l] == 0){
q.push({tmp_l, cmd_l});
check[tmp_l] = 1;
}
}
}
//R
if(cmd[cmd.length() - 1] != 'L'){
if(now == 1000){
if(check[100] == 0){
q.push({100, cmd +'R'});
check[100] = 1;
}
}
else{
int tmp_r = now % 10 * 1000 + now / 10;
string cmd_r = cmd + 'R';
if(check[tmp_r] == 0){
q.push({tmp_r, cmd_r});
check[tmp_r] = 1;
}
}
}
//D
int tmp_d = now * 2;
if(tmp_d > 9999)
tmp_d %= 10000;
string cmd_d = cmd + 'D';
if(check[tmp_d] == 0){
q.push({tmp_d, cmd_d});
check[tmp_d] = 1;
}
//S
int tmp_s = now - 1;
if(tmp_s < 0)
tmp_s = 9999;
string cmd_s = cmd + 'S';
if(check[tmp_s] == 0){
q.push({tmp_s, cmd_s});
check[tmp_s] = 1;
}
}
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
for(int i = 0; i < t; i++){
cin >> a >> b;
bfs(a);
memset(check, 0, sizeof(int) * 10000);
}
}
'BOJ' 카테고리의 다른 글
[백준/BOJ][C++] 10819번 : 차이를 최대로(DFS) (0) | 2024.04.01 |
---|---|
[백준/BOJ][C++] 6504번 : 킬로미터를 마일로(제켄도르프 정리) (1) | 2024.03.27 |
[백준/BOJ][C++] 20529번 : 가장 가까운 세 사람의 심리적 거리(비둘기집 원리) (1) | 2023.12.06 |
[백준/BOJ][C++] 7562번 : 나이트의 이동(DFS, BFS) (2) | 2023.12.05 |
[백준/BOJ][C++] 11053번 : 가장 긴 증가하는 부분 수열(DP) (1) | 2023.12.01 |