题意:求$K$的倍数中数位和的最小值。
一开始有一种思路:由于产生答案的数字可能非常大,不便枚举,考虑转化为构造一个数字可以有$xmod k=0$。然后二分答案数位和,数位DP检验是否存在,但是由于数位DP还是局限于有限大小的数字,所以并不可行。`````
套路见少了。实际上,这种构造数字满足某些条件的,一定可以把目标数字看成是一位一位写下来的,比如$114514$,可以看成逐步写下$1,1,4,5,1,4$不断$ imes 10$做加法的结果。所以考虑设$dis_i$为所有$xmod k=i$的同余类中数位和最小的,然后考虑从当前的这个状态开始写下一位,即连边$i o (10i+j)mod k$,边权$j$,然后最初从$dis_{1sim 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 ",dis[0]),0; 45 }
总结:构造数字题常用思路or最短路常用思路:逐步拆分、分解“代价”为每一步的边的边权。