D – Lunlun Number 解説 (AtCoder Beginner Contest 161)
問題概要
桁ごとに見た時、隣り合う数字の差の絶対値が 1 以下になる数を考える。小さいほうから K 番目の数を求めよ
制約
- \(1 \leq K \leq 10^5\)
考え方
制約が結構重要です。小さい方から1つずつ全探索することができれば、求められそうだということが分かります。
特殊な条件を持つ数を小さい方から全探索する時(パナソニックプログラミングコンテスト2020 D – String Equivalence など)は、深さ優先探索(DFS)を使うことは良くあり、今回もDFSで全探索することが可能です。
ただ、DFSでやると実装が面倒なので、思いつくなら公式解説にある通り Queue を利用するほうが簡単です。
解法1: DFS
DFS で小さい方の数から全探索します。
桁を固定して、小さい桁から探索するのがやりやすいと思います。
C++ での実装例
#include <bits/stdc++.h>
using namespace std;
int K;
int dfs(int k, int n, string s) { // k: 何番目の数か, n: 何桁の数を探索するか
if ((int)s.size() == n) {
if (k == K) {
cout << s << endl;
}
k++;
return k;
}
if ((int)s.size() == 0) {
for (int i = 1; i < 10; i++) {
string ns = s;
ns.push_back((char)(i + '0'));
k = dfs(k, n, ns);
}
} else {
for (int i = 0; i < 10; i++) {
if (abs((int)(s.back() - '0') - i) <= 1) {
string ns = s;
ns.push_back((char)(i + '0'));
k = dfs(k, n, ns);
}
}
}
return k;
}
int main() {
cin >> K;
int k = 1;
int n = 1;
while (k <= K) {
k = dfs(k, n, "");
n++;
}
}
解法2: Queue
Queue を上手く利用すると簡単に全探索ができます。
先頭から取り出した値を一桁増やして最後尾に追加してやると、条件を満たす数が、順番に並びます。
取り出した数を n とすると、以下のように加えれば良いです。
- n の 1 の位の数が 0 でないとき:n * 10 + (n % 10) – 1
- すべての場合について:n * 10 + (n % 10)
- n の 1 の位の数が 9 でないとき:n * 10 + (n % 10) + 1
これは幅優先探索(BFS)ということもできるでしょう。
C++ での実装例
サンプルに最大の時のケースがあるので、これを確認すると 64 bit 整数で収まることが分かります。実は string 型で保持する必要はないです。
#include <bits/stdc++.h>
using namespace std;
int K;
int main() {
cin >> K;
queue<long long> que;
for (int i = 1; i < 10; i++) {
que.push(i);
}
for (int i = 0; i < K - 1; i++) {
long long n = que.front();
que.pop();
if (n % 10 != 0) { que.push(n * 10 + (n % 10) - 1); }
que.push(n * 10 + (n % 10));
if (n % 10 != 9) { que.push(n * 10 + (n % 10) + 1); }
}
cout << que.front() << endl;
}
ディスカッション
コメント一覧
まだ、コメントがありません