同余剪枝 01组成的n的倍数

题目大意

给出一个自然数 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) x10% y = ( z ∗ 10 ) y=(z*10) y=z10% 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();
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值