POJ1426 Find The Multiple(BFS+数论)【★】

Find The Multiple

HERE

Description

Given a positive integer n, write a program to find out a nonzero multiple m of n whose decimal representation contains only the digits 0 and 1. You may assume that n is not greater than 200 and there is a corresponding m containing no more than 100 decimal digits.

Input

The input file may contain multiple test cases. Each line contains a value of n (1 <= n <= 200). A line containing a zero terminates the input.

Output

For each value of n in the input print a line containing the corresponding value of m. The decimal representation of m must not contain more than 100 digits. If there are multiple solutions for a given value of n, any one of them is acceptable.

Sample Input

2
6
19
0

Sample Output

10
100100100100100100
111111111111111111

思路

没做出来,直接引用大佬的做法。自己做的时候想的是DFS+剪枝,结果DFS超时,剪枝不会。(蒟蒻啊啊啊啊啊昂昂)其实换句话说,这题n只有200,为什么不打表呢?因为我连打表都不会
引用大佬的题解,这里是链接出处

解题思路:
首先暴力枚举肯定是不可能的 1000ms 想不超时都难,而且枚举还要解决大数问题。。
要不是人家把这题放到搜索,怎么也想不到用BFS。。。

解题方法: BFS+同余模定理
不说废话。

首先说说朴素的不剪枝搜索方法:
我以n=6为例
首先十进制数,开头第一个数字(最高位)一定不能为0,即最高位必为1

设6的 ”01十进制倍数” 为k,那么必有k%6 = 0
现在就是要用BFS求k值
1、先搜索k的最高位,最高位必为1,则此时k=1,但1%6 =1 != 0
因此k=1不是所求,存储余数 1
2、搜索下一位,下一位可能为0,即 k10+0,此时k=10,那么k%6=4
可能为1,即 k
10+1,此时k=11,那么k%6=5
由于余数均不为0,即k=10与k=11均不是所求
3、继续搜索第三位,此时有四种可能了:
对于k=10,下一位可能为0,即 k10+0,此时k=100,那么k%6=4
下一位可能为1,即 k
10+1,此时k=101,那么k%6=5
对于k=11,下一位可能为0,即 k10+0,此时k=110,那么k%6=2
下一位可能为1,即 k
10+1,此时k=111,那么k%6=3
由于余数均不为0,即k=100,k=101,k=110,k=111均不是所求
4、继续搜索第四位,此时有八种可能了:
对于k=100,下一位可能为0,即 k10+0,此时k=1000,那么k%6=4
下一位可能为1,即 k
10+1,此时k=1001,那么k%6=5
对于k=101,下一位可能为0,即 k10+0,此时k=1010,那么k%6=2
下一位可能为1,即 k
10+1,此时k=1011,那么k%6=3
对于k=110,下一位可能为0,即 k10+0,此时k=1100,那么k%6=2
下一位可能为1,即 k
10+1,此时k=1101,那么k%6=3
对于k=111,下一位可能为0,即 k10+0,此时k=1110,那么k%6=0
下一位可能为1,即 k
10+1,此时k=1111,那么k%6=1
我们发现k=1110时,k%6=0,即1110就是所求的倍数

从上面的演绎不难发现,用BFS是搜索 当前位数字 (除最高位固定为1),因为每一位都只有0或1两种选择,换而言之是一个双入口BFS
本题难点在于搜索之后的处理:对余数的处理,对大数的处理,余数与所求倍数间的关系


接下来说说处理大数问题和剪枝的方法:
首先我们简单回顾一下 朴素搜索 法:
n=6
1%6=1 (k=1)
{
(110+0)%6=4 (k=10)
{
(10
10+0)%6=4 (k=100)
{
(10010+0)%6=4 (k=1000)
(100
10+1)%6=5 (k=1001)
}
(1010+1)%6=5 (k=101)
{
(101
10+0)%6=2 (k=1010)
(10110+1)%6=3 (k=1011)
}
}
(1
10+1)%6=5 (k=11)
{
(1110+0)%6=2 (k=110)
{
(110
10+0)%6=2 (k=1100)
(11010+1)%6=3 (k=1101)
}
(11
10+1)%6=3 (k=111)
{
(11110+0)%6=0 (k=1110) 有解
(111
10+1)%6=1 (k=1111) 由于前面有解,这个余数不存储
}
}
}

从上面可以看出余数的存数顺序(逐层存储):
用数组mod[]存储余数,其中mod[0]不使用,由mod[1]开始
那么mod中的余数依次为: 1 4 5 4 5 2 3 4 5 2 3 2 3 0 共14个
即说明我们得到 余数0 之前,做了14步10的操作,那么当n值足够大的时候,是很容易出现k为大数的情况(事实上我做过统计,200以内的n,有18个n对应的k值为大数
那么我们再用int去存储k就显得不怎么明智了。
为了处理所有情况,我们自然会想到 是不是应该要用int[]去存储k的每一位?
而又由于k是一个01序列,那能不能把 乘10得到k每一位的问题 转化为 模2的操作得到k的每一位(0或1) 呢?
答案是可以的
首先我们利用 同余模定理 对得到余数的方式进行一个优化
(a
b)%n = (a%n b%n)%n
(a+b)%n = (a%n +b%n)%n
随便抽取上面一条式子为例
前一步 (11x10+1)%6=2即k=110,k%6=2
当前步 (110x10+1)%6=2
由同余模定理 (110
10+1)%6 = ((11010)%6+1%6 )%6 = ((110%6 * 10%6)%6 +1 )%6
不难发现下划线部分110%6等于 (11
10+0)%6 = 2
所以当前步(11010+1)%6可以转变为 (210+1)%6=2
很显然地,这种处理把k=110 等价于 k=2
即用 前一步操作得到的余数 代替 当前步的k值
而n在200的范围内, 余数值不可能超过3位数, 这就解决了 大数的问题
通过这种处理手法,我们只需在BFS时顺手存储一个 余数数组mod[] ,就能通过mod[i-1]得到mod[i] ,直到mod[i]==0 时结束,大大减少了运算时间
前面已经提到,n=6时,求余操作进行了14次,对应地,BFS时*10的操作也进行了14次。
令i=14,通过观察发现,i%2恰好就是 6 的倍数的最低位数字
i/2 再令 i%2 ,恰好就是 6 的倍数的 次低位数字。。。
循环这个操作,直到i=0,就能得到 6的 01倍数(一个01队列),倒序输出就是所求
这样就完成了 *10操作到 %2操作的过渡

代码

附上借鉴(抄袭 )大量题解后自己写的代码。

#include <iostream>
using namespace std;
int ans[1000000];
int main()
{
	int n;
	while (cin>>n&&n)
	{
		int i=2;
		ans[1]=1;
		for (; ans[i-1]; i++)
		{
			ans[i]=(ans[i/2]*10+i%2)%n;
		}
		i--;
		int a[100];
		int k=0;
		while (i)
		{
			a[k]=i&1;
			i>>=1;
			k++;
		}
		for (i=k-1; i>=0; i--)
		{
			cout<<a[i];
		}
		cout<<endl;
	}
	return 0;
}

代码(二刷)

/*
63ms 668kb
*/
#include <iostream>
#include <cstring>
#include <queue>
#define MAXN 205
using namespace std;
int n;
bool vis[MAXN];
struct node
{
	int mod;
	string s;
	node(int _mod=0,string _s=""):mod(_mod),s(_s) {}
};
void BFS(int n)
{
	memset(vis,true,sizeof(vis));
	int base=1;
	if (base%n==0)
	{
		cout<<base<<endl;
		return ;
	}
	queue<node> Q;
	Q.push(node(base%n,"1"));
	int a=10%n;
	int b=1;
	while (!Q.empty())
	{
		node now = Q.front();
		Q.pop();
		int mod=(now.mod*a)%n;
		if (mod==0)
		{
			cout<<now.s<<"0"<<endl;
			return ;
		}
		else if ((mod+1)==n)
		{
			cout<<now.s<<"1"<<endl;
			return ;
		}
		else
		{
			if (vis[mod])
			{
				Q.push(node(mod,now.s+"0"));
				vis[mod]=false;
			}
			if (vis[(mod+1)%n])
			{
				Q.push(node((mod+1)%n,now.s+"1"));
				vis[(mod+1)%n]=false;
			}
		}
	}
}
int main()
{
	while (cin>>n&&n)
	{
		BFS(n);
	}
	return 0;
}
/*
(a*b)%c=(a%c  *  b%c)%c
(a+b)%c=(a%c+b%c)%c
*/

tips:

题目现在看还是一脸懵逼,如果不知道同余模定理感觉还是做不出来。

引用:

https://blog.csdn.net/lyy289065406/article/details/6647917

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值