1078번: 뒤집음
어떤 수를 뒤집는다는 것은 오른쪽부터 다시 쓰는것이다. 예를 들어, 1234를 뒤집으면 4321이 되고, 100을 뒤집으면 1이 된다. (앞에 0은 무시) 어떤 수 D가 주어질 때, x – (x를 뒤집은 수)가 D가 되는
www.acmicpc.net
문제 풀이
어려운 수학적 문제이다. 여러 과정을 통해 상수 시간에 가까운 풀이를 얻었다.
이 글은 풀이를 얻기 위해 어떠한 생각을 거쳤는지 설명하는 글이다.
D가 9의 배수가 아닐 경우 만족하는 x가 없음 증명
x = a010n−1+a110n−1+...+an−1100
x를 뒤집은 수 = an−110n−1+an−210n−1+...+a0100
x - (x를 뒤집은 수) = a0(10n−1−100)+a1(10n−2−101)+... 이다.
(10n−10m)≡0 (mod9) (n,m≥0)가 항상 성립하므로, x - (x를 뒤집은 수)는 무조건 9의 배수이다.
그렇다면 D가 9의 배수이면 무조건 만족하는 x가 있을까? 정답은 No 이다. 이유는 밑에서 설명하겠다.
관찰력으로 얻은 사실
위에서 얻은 식 a0(10n−1−100)+a1(10n−2−101)+... 에서 N이 5일 때를 예로 들자.
a0(104−100)+a1(103−101)+a2(102−102)+a3(101−103)+a4(100−104)
=9999(a0−a4)+990(a1−a3) 이다. 이 식을 잘 관찰해 두 가지 아이디어를 얻었다.
첫 번째는 −9≤ai−aj≤9가 항상 성립하기 때문에, D를 얻기 위해 그리디적으로 접근해도 된다는 사실이다.
예를 들어 D = 9999인 다섯자리 X를 찾는다고 하자. 그러면 무조건 a0−a4는 1이어야 한다. 0이 된다면 뒤의 수에서 9999로 채울 수 없다.
이는 일반화가 가능하다. 이유는 위 식의 계수가 (10n−10m), (10n−1−10m+1) 순서로 작아지고,
10n−10m≥10×(10n−1−10m+1) 이기 때문이다.
여기까지 이해된다면 왜 D가 9의 배수일 때 무조건 만족하는 x가 없는지도 이해될 것이다.
두 번째는 −9≤ai−aj≤9이고, (ai,aj) 조합은 x를 작게 만들기 위해 한정지을 수 있다는 점이다.
예를 들어 52894나 10070이나 똑같은 D값을 갖는데, 오른쪽 수가 훨씬 작다.
아래 표는 X를 가장 작게 만들기 위한 (ai,aj) 값이다. 여기서 주의사항은 0일 때이다.
만약 a0−an−1이 0일 때 (0,0)으로 채우면 맨 앞이 0이 와버린다. 그러므로 이 때만 예외로 (1,1)로 채운다.
ai−aj | (ai,aj) |
---|---|
-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자리 수 (1018 미만)까지만 탐색했다. 그 이상은 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";
}
'BOJ' 카테고리의 다른 글
[백준 23840] 두 단계 최단 경로 4 (0) | 2022.07.02 |
---|---|
[백준 22021] 자동분무기 (0) | 2022.06.26 |
[백준 9661] 돌 게임7 (1) | 2022.06.22 |
[백준 1007] 벡터 매칭 - 백트래킹 (0) | 2022.06.20 |
[백준 20149] 선분 교차 3 (0) | 2022.06.18 |