【AtCoder】Small Multiple
Des
求k的所有倍数中,各个数字和最小的那一个是多少。
Sol
emmm……很巧妙的一道题
建图方法特别有意思= ̄ω ̄=
首先,考虑暴力(一个一个枚举倍数,找最小值)肯定不现实,
那,还能从哪些方面入手呢?
- 数位?
- 余数?
观察数据范围,八九不离十了
让我们尝试一下下
那怎么用余数和数位跟倍数扯上关系呢?
可以想到,数 \(x\) 的倍数模上 \(x\) 的余数是 \(0\),我们就可以用这个性质来胡乱搞一发。。
呐,令 \(dis[i]\) 为 模 \(k\) 余 \(i\) 的数 的最小数字和。
那么,我们就只用考虑在他后面添数字 0~9
,
假设我们当前状态为 \(x\), 添加的数字为 \(i\), 则添加后的表示显然为 \(y = (x * 10 + i) % k\)
\[dis[y] = min(dis[x] + i)
\]
其实就是跑一个最短路。。。
初始条件就是,把所有 \(dis\) 赋成最大值,然后 \(dis[i] = i(1 \le i \le 9)\) —— 只有一位数字时的情况。
然后就最短路模板直接来
很明显,答案就是 \(dis[0]\)
Code
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N = 1e5 + 5;
int dis[N], k;
bool vis[N];
void bfs() {
queue<int> q;
memset(dis, 0x3f, sizeof(dis));
for(int i = 1; i <= 9; i ++) q.push(i), dis[i] = i, vis[i] = 1;
int now, ver;
while(!q.empty()) {
now = q.front(), q.pop(), vis[now] = 0;
for(int i = 0; i <= 9; i ++) {
ver = (now * 10 + i) % k;
if(dis[ver] > dis[now] + i) {
dis[ver] = dis[now] + i;
if(!vis[ver]) q.push(ver), vis[ver] = 1;
}
}
}
}
int main() {
scanf("%d", &k);
bfs();
printf("%d\n", dis[0]);
return 0;
}