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 nd∣n且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;
}