CF1070A Find a Number

CF1070A Find a Number

洛谷传送门

题意翻译

给定两个数d(1\le d \le 500)d(1≤d≤500)和s(1\le s\le 5000)s(1≤s≤5000),找出最小数nn使得d\mid ndn且nn的各个位数之和为ss


题解:

暴力的思路肯定就是枚举\(d\)的所有倍数。

看到第二个样例之后绝望。然后又发现了-1的情况,所以觉得正解应该是搜索。

然后还要找最小值,所以感觉应该是BFS,然后觉得裸的BFS转不了,所以应该加个记忆化。

嗯,于是正解的思路就拿出来了。BFS+记忆化。

我们设\(pre[d][s]\)为当前枚举到的各位和为\(s\),当前数字对\(d\)取模结果为\(d\)上一个\(d,s\)和位数。这样就可以用递归输出答案。

为什么在状态设置上要加上对\(d\)取模呢?因为当前数字可能很大,但是其对\(d\)取模的结果是当前状态下唯一的,所以可以保证正确性。

代码:

#include<bits/stdc++.h>
using namespace std;
int d,s,pre[505][5005][3];
bool v[505][5005];
queue<int>qx,qy;
void bfs()
{
    v[0][0]=1;
    qx.push(0);
    qy.push(0);
    while(!qx.empty())
	{
        int x=qx.front();
		qx.pop();
        int y=qy.front();
		qy.pop();
        for(int i=0;i<=9;i++)
		{
            int xx=(10*x+i)%d,yy=y+i;
            if(v[xx][yy]||yy>s)
                continue;
            v[xx][yy]=1;
            pre[xx][yy][0]=x;
            pre[xx][yy][1]=y;
            pre[xx][yy][2]=i;
            qx.push(xx);
            qy.push(yy);
        }
    }
}
void print(int x,int y)
{
    if(!x&&!y)
        return;
    print(pre[x][y][0],pre[x][y][1]);
    printf("%d",pre[x][y][2]);
}
int main()
{
	scanf("%d%d",&d,&s);
    bfs();
    if(v[0][s]==0)
        puts("-1");
    else 
        print(0,s);
    return 0;
}
posted @ 2020-11-26 19:58  Seaway-Fu  阅读(85)  评论(0编辑  收藏  举报