ABC007D Small Multiple[最短路]

题意:求$K$的倍数中数位和的最小值。

一开始有一种思路:由于产生答案的数字可能非常大,不便枚举,考虑转化为构造一个数字可以有$x\mod k=0$。然后二分答案数位和,数位DP检验是否存在,但是由于数位DP还是局限于有限大小的数字,所以并不可行。`````

套路见少了。实际上,这种构造数字满足某些条件的,一定可以把目标数字看成是一位一位写下来的,比如$114514$,可以看成逐步写下$1,1,4,5,1,4$不断$\times 10$做加法的结果。所以考虑设$dis_i$为所有$x\mod k=i$的同余类中数位和最小的,然后考虑从当前的这个状态开始写下一位,即连边$i\to (10i+j)\mod k$,边权$j$,然后最初从$dis_{1\sim 9}$(首位)为源点跑最短路,求$dis_0$即可。

atcoder的公式题解给的思路差不多,对于每个数,可以选择加$1$或者乘$10$,前者连边权为$1$,后者连边权$0$,和上面差不多,然后跑$01$最短路。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<queue>
 7 #define dbg(x) cerr << #x << " = " << x <<endl
 8 #define dbg2(x,y) cerr<< #x <<" = "<< x <<"  "<< #y <<" = "<< y <<endl
 9 using namespace std;
10 typedef long long ll;
11 typedef double db;
12 typedef pair<int,int> pii;
13 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
14 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
15 template<typename T>inline bool MIN(T&A,T B){return A>B?(A=B,1):0;}
16 template<typename T>inline bool MAX(T&A,T B){return A<B?(A=B,1):0;}
17 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
18 template<typename T>inline T read(T&x){
19     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
20     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
21 }
22 const int N=1e5+7;
23 struct thxorz{int nxt,to,w;}G[N*11];
24 int Head[N],tot;
25 int n;
26 inline void Addedge(int x,int y,int z){G[++tot].to=y,G[tot].nxt=Head[x],Head[x]=tot,G[tot].w=z;}
27 int dis[N];
28 priority_queue<pii,vector<pii>,greater<pii> > pq;
29 #define y G[j].to
30 inline void dij(){
31     memset(dis,0x3f,sizeof dis);for(register int i=1;i<10;++i)MIN(dis[i%n],i),pq.push(make_pair(dis[i%n],i%n));
32     while(!pq.empty()){
33         int d=pq.top().first,x=pq.top().second;pq.pop();
34         if(d^dis[x])continue;//dbg2(x,d);
35         if(x==0)return;
36         for(register int j=Head[x];j;j=G[j].nxt)if(MIN(dis[y],d+G[j].w))pq.push(make_pair(dis[y],y));
37     }
38 }
39 #undef y
40 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
41     read(n);
42     for(register int i=1;i<n;++i)for(register int j=0;j<10;++j)Addedge(i,(10*i+j)%n,j);
43     dij();
44     return printf("%d\n",dis[0]),0;
45 }
View Code

总结:构造数字题常用思路or最短路常用思路:逐步拆分、分解“代价”为每一步的边的边权。

posted @ 2019-10-08 15:21  Ametsuji_akiya  阅读(165)  评论(0编辑  收藏  举报