D125 同余最短路 Dijkstra 算法 AT_arc084_b [ABC077D] Small Multiple

D125 同余最短路 AT_arc084_b [ABC077D] Small Multiple_哔哩哔哩_bilibili

 

AT_arc084_b [ABC077D] Small Multiple - 洛谷

给定一个整数 K,求一个 K 的正整数倍 S,使得 S 的数位之和最小,输出数位之和。

思路

考虑一个整数 $x$ 变成更大的整数 $10x+j,\;j\in [0,9]$,数位之和增加 $j$ 

建图表示:$x\overset{j}{\rightarrow} 10x+j,\;j\in [0,9]$,边权就是数位和的增量

所有整数按模 $k$ 可以分为 $k$ 个同余类,模 $k$ 建图:$i\overset{j}{\rightarrow} (10i+j)\%k,\;i\in [0,k-1],\;j\in [0,9]$

注意:起点不是 0 点,起点是 $k$ 点,可以从 $k$ 点向 $(10k+1)\%k$ 点连一条边权为 $1$ 的边

跑同余最短路后,$d[0]$ 表示模 $k$ 为 $0$ 的点到起点 $k$ 的最短路(即数位之和的最小值)

相关板子:

D123【模板】同余最短路 Dijkstra 算法 P2662 [WC2002] 牛场围栏 - 董晓 - 博客园

 

// 同余最短路 Dijkstra 算法 O(MlogN)
#include<bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;

const int N=100010,M=N*10;
int idx,h[N],to[M],ww[M],ne[M];
void add(int u,int v,int w){
  to[++idx]=v;ww[idx]=w;ne[idx]=h[u];h[u]=idx;
}
int k,d[N];

void dijkstra(int s){
  memset(d,0x3f,sizeof(d)); d[s]=0;
  priority_queue<pii,vector<pii>,greater<pii> >q;
  q.push({0,s});
  while(!q.empty()){
    auto [dd,u]=q.top();q.pop();
    if(dd!=d[u]) continue;
    for(int i=h[u];i;i=ne[i]){
      int v=to[i],w=ww[i];
      if(d[v]>d[u]+w){
        d[v]=d[u]+w;
        q.push({d[v],v});
      }
    }
  }
}
signed main(){
  scanf("%lld",&k);
  for(int i=0;i<k;++i)for(int j=0;j<10;++j)add(i,(i*10+j)%k,j);
  add(k,(k*10+1)%k,1); //起点k
  
  dijkstra(k);
  cout<<d[0];
}

 

更简单的做法,使用 $01BFS$ 求最短路

假设 $x\; mod\; k=0$,且 $x$ 的数位之和 $s$ 最小

从 $x=1,s=1$ 开始扩展,有两种操作:

1. $x∗=10$,$x$ 的数位之和 $s$ 不变,代价为 0,把状态 $(x,s)$ 放在队首

2. $x+=1$,$x$ 的数位之和变为 $s+1$,代价为 1,把状态 $(x,s+1)$ 放在队尾

每次操作保存 $x\%k$ 的状态即可

第一次扩展到 $x\; mod\; k=0$,那么对应 $s$ 就是答案

// 同余最短路 01BFS O(N)
#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
bool vis[N];
int k;

void bfs(){
  deque<pair<int,int> > q;
  q.push_front({1,1}); //从{1,1}开始扩展
  
  while(!q.empty()){
    auto [x,s]=q.front(); q.pop_front();
    if(vis[x]) continue; vis[x]=true;
    if(x==0){
      printf("%d\n",s);
      return;
    }
    q.push_front({x*10%k,s});
    q.push_back({(x+1)%k,s+1});
  }
}
int main(){
  scanf("%d",&k);
  bfs();
}

 

posted @ 2026-04-05 13:09  董晓  阅读(90)  评论(0)    收藏  举报