You are given two positive integers d and s. Find minimal positive integer n which is divisible by d and has sum of digits equal to s
.
Input
The first line contains two positive integers d and s
(1≤d≤500,1≤s≤5000) separated by space.
Output
Print the required number or -1 if it doesn't exist.
给出两个正整数 d 和 s ,求最小的正整数 n ,使得 n 为 d 的倍数,且 n 的每一位加起来等于 s 。
首先,不难想到搜索,但是 n 可能很大,以至于超出 long long 的范围,显然这个题还没有复杂到综合考察大数运算。所以在这里使用一个带有数论知识的DP。
说明:
结构体数组 DP[i][j]
i 为 余数,j 为 当前数字的位数和,DP[I][J]表示 一个记录,这个记录包含当前数字的最后一位 dig,以及导出当前状态的初始状态。
标记数组 exist[i][j]:
对应的 DP[I][J] 表示的状态是否存在
考虑对任意正整数 x ,记 i (0:9),在 x 后添加一位可以的到所有的正整数为 x*10+i。若已知x对d的余数mod=x%d,x的位数和sum,那么,可以将上述过程视为如下的状态转移
DP[mod][sum]->DP[(10*mod+i)%d][sum+i]
根据DP数组的定义,转移后的状态 DP[(10*mid+i)%d][sum+i] 记录中 dig 为 i ,导出当前状态的的初始状态为(mod,sum)。
因此,查询是否存在一个 d 的倍数 n ,使得 n 的各位数和为 s ,只需查询 DP[0][s] 是否可被导出。
若可被导出,可根据 DP 数组记录的前一状态进行回溯,从而得到完整的路径
此外,证明这样得到的答案是最小的。对于到达状态DP[i][j]来说,可分为如下两类:
- 从存在最小值的节点到达
若已存在最小状态,那么再从当前状态到达DP[i][j],所对应的数字必然大于从最小状态到达时所对应的数字 - 从未存在最小值的节点到达
根据压入顺序,当前取出的状态对应的数字一定时在队列中所有对应数字最小的,因此,从当前状态到达DP[i][j]对应的数字也一定时最小的。
#include <stdio.h>
#include <cstring>
#include <climits>
#include <algorithm>
#include <queue>
#include <utility>
#define Pair pair<int,int>
#define rep(index,star,finish) for(register int index = star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
#define fi first
#define se second
#define ll long long
#define re return
#define LIM 5025
using namespace std;
struct record{
int reminder,least; //point of last reminder and least
char dig; //the digit of now situation
record():dig(0){}
record(int r,int l,char d):reminder(r),least(l),dig(d){}
};
int d,s;
bool exist[505][5005];
record Record[505][5005];
queue<Pair> Q; //pair means: first->reminder second->min sum
void getNum(int nRem,int nSum);
int main(){
scanf("%d %d",&d,&s);
memset(exist,false,sizeof exist);
exist[0][0]=true;
while(!Q.empty())
Q.pop();
Q.push(make_pair(0,0));
Pair now;
while(!Q.empty()){
now=Q.front();
Q.pop();
rep(i,0,10){
int nRem=(now.first*10+i)%d; //next reminder
int nSum=now.second+i; //next min sum
if(nSum>s)
break;
if(!exist[nRem][nSum]){
Record[nRem][nSum]=record(now.first,now.second,i+'0');
exist[nRem][nSum]=true;
Q.push(make_pair(nRem,nSum));
}
}
}
if(exist[0][s]){
getNum(0,s);
printf("\n");
}else
printf("-1\n");
re 0;
}
void getNum(int nRem,int nSum){
if(Record[nRem][nSum].dig!=0){
getNum(Record[nRem][nSum].reminder,Record[nRem][nSum].least);
printf("%c",Record[nRem][nSum].dig);
}
}