1. 题面梗概
定义一个 “好数字”,它的数位从个位到高位是非递增的。也就是从左往右读,数字是不减的,比如 112389,777 这种。
求最小的 N 的倍数,且它是 "好数字",如果不存在就输出 −1。
2. 逻辑推导
我第一想法是枚举 N 的倍数,然后判断是否为 “好数字”,但是想想都知道,这个复杂度太高了,所以我转而思考搜索这个角度切入。
我们需要找到最小的 X,满足 X≡0(modN)。最小意味着两点,位数越小越好,字典序越小越好。
最小的那个答案,那不就可以用 BFS 搜索吗?以数字的长度和字典序为状态搜索,第一个搜到的就是最小的答案。
BFS 的特性就是搜索到最短路(最小值),而 DFS 的特性是判断是否可行。
同时题目还给了我们一个信息,答案可能比 264 还大,这就暗示我们可以只关注余数,如果某次搜索得到的余数为 0,那就是答案 X。
2.1 同余最短路
没想到吧,这个破题居然还可以转化为图论问题。
节点: (remain,last_dight),记录余数和最后一位数字。
边:(r,d)→((r×10+nd)(modN)),nd),其中 nd≥d。
细节:因为要输出整个数字,所以我们需要记录每个状态的前驱用于还原。
2.2 数位DP
其实在写完复盘的时候,我突然想到,这种类型能不能用数位DP来解决呢?因为有数位约束,又有余数。
但是数位DP的常态是:给定一个上限 R,问 [0,R] 之间有多少个都符合条件的数。
本题的最大问题就是,没有给出这个上限 R,因此没法开状态。
3. 代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| #include <bits/stdc++.h> using namespace std; using ll = long long;
const int maxn = 3e6 + 5;
struct State { int rem, dig; } pre[maxn][10];
bool vis[maxn][10];
int n;
void solve( ) { cin >> n;
queue< State > q; for( int d = 1; d <= 9; ++ d ) { int r = d % n; if( vis[r][d] ) continue; vis[r][d] = 1; q.push({ r, d }); pre[r][d] = { -1, -1 }; }
State ans = { -1, -1 };
while( !q.empty( ) ) { auto [r, d] = q.front( ); q.pop( );
if( r == 0 ) { ans = { r, d }; break; }
for( int nd = d; nd <= 9; ++ nd ) { int nr = ( r * 10 + nd ) % n; if( vis[nr][nd] ) continue; vis[nr][nd] = 1; q.push({ nr, nd }); pre[nr][nd] = { r, d }; } }
if( ans.rem == -1 ) { cout << -1 << '\n'; return; }
vector< int > res; while( ans.rem != -1 ) { res.push_back( ans.dig ); ans = pre[ans.rem][ans.dig]; } reverse( res.begin( ), res.end( ) ); for( int d : res ) cout << d; cout << '\n';
}
int main( ) { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int _t = 1; while( _t -- ) solve( ); return 0; }
|
4. 复盘