BOJ

[백준 1078] 뒤집음

ggyuchive 2022. 6. 27. 13:26

 

1078번: 뒤집음

어떤 수를 뒤집는다는 것은 오른쪽부터 다시 쓰는것이다. 예를 들어, 1234를 뒤집으면 4321이 되고, 100을 뒤집으면 1이 된다. (앞에 0은 무시) 어떤 수 D가 주어질 때, x – (x를 뒤집은 수)가 D가 되는

www.acmicpc.net

문제 풀이

어려운 수학적 문제이다. 여러 과정을 통해 상수 시간에 가까운 풀이를 얻었다.

이 글은 풀이를 얻기 위해 어떠한 생각을 거쳤는지 설명하는 글이다.

D가 9의 배수가 아닐 경우 만족하는 x가 없음 증명

x = $ a_{0}10^{n-1} + a_{1}10^{n-1} + ... + a_{n-1}10^0 $

x를 뒤집은 수 = $ a_{n-1}10^{n-1} + a_{n-2}10^{n-1} + ... + a_{0}10^0 $

x - (x를 뒤집은 수) = $ a_{0}(10^{n-1}-10^0) + a_{1}(10^{n-2}-10^1) + ... $ 이다.

$ (10^n-10^m) \equiv 0 $ $ (mod 9) $ $ (n, m \geq 0) $가 항상 성립하므로, x - (x를 뒤집은 수)는 무조건 9의 배수이다.

그렇다면 D가 9의 배수이면 무조건 만족하는 x가 있을까? 정답은 No 이다. 이유는 밑에서 설명하겠다.

관찰력으로 얻은 사실

위에서 얻은 식 $ a_{0}(10^{n-1}-10^0) + a_{1}(10^{n-2}-10^1) + ... $ 에서 N이 5일 때를 예로 들자.

$ a_{0}(10^4-10^0) + a_{1}(10^3-10^1) + a_{2}(10^2-10^2) + a_{3}(10^1-10^3) + a_{4}(10^0-10^4) $

$ = 9999(a_{0}-a_{4}) + 990(a_{1}-a_{3}) $ 이다. 이 식을 잘 관찰해 두 가지 아이디어를 얻었다.

첫 번째는 $ -9 \leq a_{i}-a_{j} \leq 9 $가 항상 성립하기 때문에, D를 얻기 위해 그리디적으로 접근해도 된다는 사실이다.

예를 들어 D = 9999인 다섯자리 X를 찾는다고 하자. 그러면 무조건 $ a_{0}-a_{4} $는 1이어야 한다. 0이 된다면 뒤의 수에서 9999로 채울 수 없다.

이는 일반화가 가능하다. 이유는 위 식의 계수가 $(10^n-10^m)$, $(10^{n-1}-10^{m+1})$ 순서로 작아지고,
$10^n-10^m \geq 10 \times (10^{n-1}-10^{m+1})$ 이기 때문이다.

여기까지 이해된다면 왜 D가 9의 배수일 때 무조건 만족하는 x가 없는지도 이해될 것이다.

두 번째는 $ -9 \leq a_{i}-a_{j} \leq 9 $이고, $ (a_{i},a_{j}) $ 조합은 x를 작게 만들기 위해 한정지을 수 있다는 점이다.

예를 들어 52894나 10070이나 똑같은 D값을 갖는데, 오른쪽 수가 훨씬 작다.

아래 표는 X를 가장 작게 만들기 위한 $(a_{i},a_{j})$ 값이다. 여기서 주의사항은 0일 때이다.
만약 $a_{0}-a_{n-1}$이 0일 때 (0,0)으로 채우면 맨 앞이 0이 와버린다. 그러므로 이 때만 예외로 (1,1)로 채운다.

$a_{i}-a_{j}$ $(a_{i},a_{j})$
-9 (0,9)
-8 (0,8)
-7... (0,7)
0 (0,0),(1,1)
...7 (7,0)
8 (8,0)
9 (9,0)

답이 될 수 있는 최댓값

아래 있는 최종 코드에서 x를 18자리 수 ($10^{18}$ 미만)까지만 탐색했다. 그 이상은 D가 주어진 값보다 무조건 커지기 때문이다.

소스 코드

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main() {
    ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);

    ll d; cin >> d;
    ll ten[18]; ten[0]=1;
    for (int i = 1; i < 18; i++) ten[i] = ten[i-1] * 10;

    for (int i = 2; i <= 18; i++) {
        int n = (i/2);
        vector<ll> ten1;
        vector<ll> v1;
        ll t = d;
        for (int j = 0; j < n; j++) {
            ten1.push_back((ten[i-j-1]-ten[j]) / ten[j]);
        }
        for (int j = 0; j < n; j++) {
            int a;
            if (t >= 0) a = (10-t%10)%10;
            else a = (-10-t%10)%10;
            v1.push_back(a);
            t = (t - a * ten1[j])/10;
        }
        if (t == 0) {
            ll ans = 0;
            for (int j = 0; j < n; j++) {
                if (v1[j] == 0 && j == 0) ans += ten[i-1]+ten[0];
                if (v1[j] > 0) ans += (ten[i-1-j] * v1[j]);
                if (v1[j] < 0) ans -= (ten[j] * v1[j]);
            }
            cout << ans;
            return 0;
        }
    }
    cout << "-1";
}