E - Find The Multiple(巧用for循环搜索)
题目地址:https://vjudge.net/contest/422351#problem/E
题目描述:
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.
题目大意:
给定一个数字n,输出一个由0和1组成的数字使其能够整除n。
2 6 19 0Sample Output
10
100100100100100100
111111111111111111
一、题目简述
这一题第一眼看到样例输出,显然是不是使用int就能够完全储存下来的,至于能否使用long long就把它做出来,我们需要进行考虑,并且这题更大的价值在于思考如果n的值变大的话,该如何进行搜索。
二、普通dfs
2-1、思路:
我们可以尝试使用打表的方式进行检验,最后发现在1 <= n <= 200的情况下,unsigned long long是可以储存下所有答案的,因此可以使用普通dfs过这道题。
2-2、代码:
inline void dfs(ull num,ull ans,ull k) { if(flag) return ; if(num%ans==0) { sum=num; flag=1; return ; } if(k==19) return; dfs(num*10,ans,k+1); dfs(num*10+1,ans,k+1); } int main() { while(1) { read(n); if(0==n) break; flag=0; dfs(1,n,0); printf("%llu\n",sum); } return 0; }
三、改进搜索
3-1、思路
上述方式只是基于本题的一种解。诚然,只要做出来的代码就是好代码,不过作为扩展,我们可以考虑一下如何将n的可解范围扩大。
首先,如果将某个数的二进制右移一位,在十进制的视角下就相当于在原数的基础上乘以10,在二进制下的右移一位等于原数字在十进制下乘以2。
例如:2的二进制是10,4的二进制是100。
既然如此,我们可以尝试枚举每一个数字的二进制,并将其转为只有1和0的十进制数字,这样一来,我们的搜索顺序就是从小到大的顺序,而不是像上种方法一样不断的乘以十。从某种意义上来说,这有点像是dp。
for(i=1;;i++) { a[i]=a[i>>1]*10+i%2; if(a[i]%n==0) { printf("%llu\n",a[i]); break; } }
3-2、利用取模性质扩大范围
可以看到,我们依然用了unsigned long long的大数字,并不能够实际优化多少,这个时候就要用到取模的性质了:
(a+b)%p=(a%p+b%p)%p、(a*b)%p=(a%p*b%p)%p、(a-b)%p=(a%p-b%p)%p
我来翻译一下这三个公式:“你尽管取模,最后余数有变化算我输.jpg”
如此我们就能够扩大范围了。
3-3代码:
int main() { while(1) { read(n); if(0==n) break; for(i=1;;i++) { a[i]=(a[i>>1]*10+i%2)%n; if(a[i]==0) break; } int ans=i; int cnt=0; while(ans) { a[++cnt]=ans&1; ans>>=1; } for(cnt;cnt>=1;cnt--) printf("%d",a[cnt]); printf("\n"); } return 0; }