题目大意
给出一个自然数 N ( ≤ 1000000 ) N(\le1000000) N(≤1000000),找出 M M M,使 M M M是 N N N的倍数,并且 M M M的十进制中只包含 0 0 0和 1 1 1。求出最小的 M M M。
解题思路
由于
N
N
N可能等于
1000000
1000000
1000000,所以暴力是不可能了,我们要考虑找另外的做法。我们发现,对于
M
M
M,它只包含
0
0
0和
1
1
1,所以我们可以使用广搜的方法,对于一个形式合法但不是
N
N
N的倍数的数,我们可以在它的后面加上
0
0
0或者
1
1
1,(即乘
10
10
10或乘
10
+
1
10+1
10+1),存入队列。但这样我们发现可能会出现非常大的数的情况,并且用去很多时间,这时怎么办呢?利用同余定理进行剪枝优化。 我们知道,对于两个数
x
x
x和
y
y
y,若它们%
z
z
z都等于一个相同的数,那么它们同时乘一个相同的数再%
z
z
z结果依然相同。因此对于
x
x
x和
y
y
y,我们只需要选择较小的那个存入队列就可以实现了。但这时又有一个问题,就算我们选择了最小的
x
x
x或
y
y
y,但结果依旧很大,(例如
N
=
99999
N=99999
N=99999时,
M
=
111111111111111111111111111111111111111111111
M=111111111111111111111111111111111111111111111
M=111111111111111111111111111111111111111111111)。 这时我们思考,如果我们每次添加
0
0
0和
1
1
1时,都把添加的数记录下来,并记录是从哪个数得到的,最后得出答案时再依次输出,就可以解决了,并且再次利用同余定理:若
x
x
x%
y
y
y为
z
z
z,则
(
x
∗
10
)
(x*10)
(x∗10)%
y
=
(
z
∗
10
)
y=(z*10)
y=(z∗10)%
y
y
y。因此,每次获得一个新数时,先记录加上的数字,然后后直接%
N
N
N,直到找到%
N
N
N为
0
0
0的数后输出。
注意最开始队列里的数应为
1
1
1,因为
0
0
0开头没有意义。
代码
#include<bits/stdc++.h>
using namespace std;
struct ap{
int p,k;
}a[1000005];
queue <int> q;
int n;
bool v[1000005];
void print(int x)
{
if(x!=-1) //如果不是开头
{
print(a[x].p);//继续找
cout<<a[x].k;//输出这一次加上的数字
}
}
void bfs()
{
int x;
a[1].p=-1;//记录1不是由任何数得到的
a[1].k=1;//在空数后加上1
v[1]=1;//记录1得到过
q.push(1);
while(!q.empty())
{
x=q.front();
q.pop();
if(!x) break;//如果找到了
int k=(x*10)%n;//加上0
if(!v[k])
{
q.push(k);
v[k]=1;//记录得到过
a[k].k=0;//记录加上的数
a[k].p=x;//由x得到
}
k=(x*10+1)%n;//加上1
if(!v[k])
{
q.push(k);
v[k]=1;
a[k].k=1;
a[k].p=x;
}
}
print(x);
}
int main()
{
cin>>n;
bfs();
}