codeforces 1070A FindANumber

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]来说,可分为如下两类:

  1. 从存在最小值的节点到达
    若已存在最小状态,那么再从当前状态到达DP[i][j],所对应的数字必然大于从最小状态到达时所对应的数字
  2. 从未存在最小值的节点到达
    根据压入顺序,当前取出的状态对应的数字一定时在队列中所有对应数字最小的,因此,从当前状态到达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);
    }
}

猜你喜欢

转载自blog.csdn.net/white_156/article/details/83759465