본문 바로가기

Coding Test

백준 9019 DSLR (C++)

https://www.acmicpc.net/problem/9019

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

bool visit[10000];

int main(){

    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);


    int t;
    cin>>t;

    for(int test=0;test<t;test++){

        int s, e;
        cin>>s>>e;
    
        fill_n(visit, 10000, false);

        queue<pair<int,string>> q;
        q.push(make_pair(s, ""));

        while(!q.empty()){
            int cur=q.front().first;
            string cmd=q.front().second;

            q.pop();

            if(cur==e){
                cout<<cmd<<"\n";
                break;
            }


            //D
            if(!visit[(cur*2)%10000]){
                visit[(cur*2)%10000]=true;
                q.push(make_pair((cur*2)%10000, cmd+"D"));
            }

            //S
            if(!visit[cur-1<0?9999:cur-1]){
                visit[cur-1<0?9999:cur-1]=true;
                q.push(make_pair(cur-1<0?9999:cur-1, cmd+"S"));
            }

            //L
            if(!visit[(cur*10)%10000+cur/1000]){
                visit[(cur*10)%10000+cur/1000]=true;
                q.push(make_pair((cur*10)%10000+cur/1000, cmd+"L"));
            }

            //R
            if(!visit[(cur%10)*1000+cur/10]){
                visit[(cur%10)*1000+cur/10]=true;
                q.push(make_pair((cur%10)*1000+cur/10, cmd+"R"));
            }
        }
        

    }

    return 0;
}

1. 문제

네 개의 명령어 D, S, L, R 을 이용하는 간단한 계산기가 있다. 이 계산기에는 레지스터가 하나 있는데, 이 레지스터에는 0 이상 10,000 미만의 십진수를 저장할 수 있다. 각 명령어는 이 레지스터에 저장된 n을 다음과 같이 변환한다. n의 네 자릿수를 d1, d2, d3, d4라고 하자(즉 n = ((d1 × 10 + d2) × 10 + d3) × 10 + d4라고 하자)

  1. D: D 는 n을 두 배로 바꾼다. 결과 값이 9999 보다 큰 경우에는 10000 으로 나눈 나머지를 취한다. 그 결과 값(2n mod 10000)을 레지스터에 저장한다.
  2. S: S 는 n에서 1 을 뺀 결과 n-1을 레지스터에 저장한다. n이 0 이라면 9999 가 대신 레지스터에 저장된다.
  3. L: L 은 n의 각 자릿수를 왼편으로 회전시켜 그 결과를 레지스터에 저장한다. 이 연산이 끝나면 레지스터에 저장된 네 자릿수는 왼편부터 d2, d3, d4, d1이 된다.
  4. 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로 변환하기 위해 필요한 최소한의 명령어 나열을 출력한다. 가능한 명령어 나열이 여러가지면, 아무거나 출력한다.

2. 풀이

처음 숫자에서 네 종류의 연산을 할 수 있다. 즉, 4개의 서로 다른 노드로 탐색할 수 있다는 뜻이다. 이에 따라 BFS를 돌려 풀게 되었다.

        fill_n(visit, 10000, false);

        queue<pair<int,string>> q;
        q.push(make_pair(s, ""));

10000개의 방문 배열을 잡아 같은 숫자를 두 번 만드는 일이 없도록 한다. 큐에는 현재 숫자와, 현재까지 올 때 사용한 연산의 히스토리를 저장한다.

            int cur=q.front().first;
            string cmd=q.front().second;

            q.pop();

            if(cur==e){
                cout<<cmd<<"\n";
                break;
            }

연산 후 해당 숫자를 찾았다면 히스토리를 출력하고 BFS를 종료한다.

            //D
            if(!visit[(cur*2)%10000]){
                visit[(cur*2)%10000]=true;
                q.push(make_pair((cur*2)%10000, cmd+"D"));
            }

            //S
            if(!visit[cur-1<0?9999:cur-1]){
                visit[cur-1<0?9999:cur-1]=true;
                q.push(make_pair(cur-1<0?9999:cur-1, cmd+"S"));
            }

            //L
            if(!visit[(cur*10)%10000+cur/1000]){
                visit[(cur*10)%10000+cur/1000]=true;
                q.push(make_pair((cur*10)%10000+cur/1000, cmd+"L"));
            }

            //R
            if(!visit[(cur%10)*1000+cur/10]){
                visit[(cur%10)*1000+cur/10]=true;
                q.push(make_pair((cur%10)*1000+cur/10, cmd+"R"));
            }

그렇지 않다면 각 연산에 대해 BFS를 수행한다.

  • D연산: 문제에서 주어진대로 현재 숫자에 2를 곱하고 mod 10000 연산을 진행한다.
  • S연산: 마찬가지로 1을 빼되, 0보다 작아지면 9999를 넣는다.
  • L연산: Left shift 연산이다. 10을 곱하여 mod 10000을 하면 뒤의 세 자리가 앞으로 이동한다. 여기에 현재 숫자를 1000으로 나눈 몫인 천의 자리 숫자를 더하여 만들 수 있다.
  • R연산: Right shift 연산이다. 현재 숫자를 mod 10하여 1000을 곱하면 일의 자리 숫자가 천의 자리 숫자로 이동한다. 이에 현재 숫자를 10으로 나눈 몫을 더해주면서 만들 수 있다.

각 연산에 대하여 해당 숫자를 방문한 적이 없다면 visit[결과]=true로 만들어주고, 히스토리에 해당 연산을 붙여준다.

 

위의 BFS를 끝내면 정답을 구할 수 있다.

'Coding Test' 카테고리의 다른 글

백준 1963 소수 경로 (Java)  (1) 2024.10.02
백준 12886 돌 그룹 (C++)  (1) 2024.09.27
백준 21923 곡예 비행 (C++)  (0) 2024.09.23
백준 2073 수도배관공 (C++)  (1) 2024.09.22
백준 2758 로또 (C++)  (1) 2024.09.11