题意:给定一个整数n,求n的一个倍数,要求这个倍数只含0和1
参考博客:点我
解法一:普通的BFS(用G++能过但C++会超时)
从小到大搜索直至找到满足条件的数,注意最高位一定为1
假设 n=6 k即为当前所求的目标数,不满足条件则进一步递推 (i 为层数(深度),在解法二的优化中体现,此时可以不管)
1%6=1 (k=1) i=1
{
(1*10+0)%6=4 (k=10) i=2
{
(10*10+0)%6=4 (k=100) i=4
{
(100*10+0)%6=4 (k=1000) i=8
(100*10+1)%6=5 (k=1001) i=9
}
(10*10+1)%6=5 (k=101) i=5
{
(101*10+0)%6=2 (k=1010) i=10
(101*10+1)%6=3 (k=1011) i=11
}
}
(1*10+1)%6=5 (k=11) i=3
{
(11*10+0)%6=2 (k=110) i=6
{
(110*10+0)%6=2 (k=1100) i=12
(110*10+1)%6=3 (k=1101) i=13
}
(11*10+1)%6=3 (k=111) i=7
{
(111*10+0)%6=0 (k=1110) i=14 有解
(111*10+1)%6=1 (k=1111) 由于前面有解,这个余数不存储
}
}
}
可以看到,如此反复枚举下去,直至找到目的数k为止,注意若当前k不满足条件,则将k*10 和k*10 + 1入队以便进一步判断
以下是解法一的代码,用G++能过,但用C++超时(我是在hust交的),不知道为什么!
/* POJ 1426 Find The Multiple BFS 用C++超时但G++过了*/ #include <cstdio> #include <queue> using namespace std; /*返回最大能整除n的只有1和0组成的数*/ long long bfs(int n){ long long tmp; queue<long long> q; q.push(1); while (!q.empty()){ tmp = q.front(); q.pop(); if (tmp % n == 0){ return tmp; } q.push(tmp * 10); q.push(tmp * 10 + 1); } return 0; } int main() { int n; while (scanf("%d", &n) == 1 && n){ printf("%lld ", bfs[n]); } return 0; }
解法二:优化的BFS(C++能过)
由于C++超时,于是我参考了以下别人的代码,地址上面已给出,但为了加深记忆,还是自己写一遍!
同余定理的应用:
(a+b)%n = (a%n + b%n)%n
(a*b)%n = (a%n * b%n)%n
在解法一中,由于一直*10, k有可能出现越界,具体请参考原博客,在这里只写余数优化过程:
取解法一中的任意式子:
前一层: mod = (11*10 + 0)%6 = 2, k = 110, i = 6, k % 6 = 2
后一层: mod = (k'*10 + 0) %6 //注意这里的k'为上一层的k,即110, 另此层k为110*10 + 0, i 为6 * 2
=( (k'*10)%6 + 0%6 ) % 6
=( (k'%6 * 10%6) % 6 + 0%6) % 6 //k'%6即为上一层的mod,记为mod', 在这个例子中就是2, 另k'%6 = mod' = mod'%6
=( (mod'6 * 10%6) % 6 + 0%6) % 6
=( (mod' * 10) % 6 + 0 % 6) % 6
= (mod'*10 + 0) % 6; //即当前层的mod=前一层的mod*10 + 0 (具体是0还是1和i有关) 因为0、1是循环取的
为了加强理解,再推导一遍i为奇数的过程
前一层:mod = (11*10 + 0)%6 = 2, k = 110, i = 6, k % 6 = 2
后一层: mod = (k'*10 + 1) % 6 //此层 k = 110*10+1, i 为6*2+1
= ( (k'%6 * 10%6) % 6 + 1 % 6) % 6
= ( mod' * 10 + 1) % 6 //即当前层的mod=前一层的mod*10 + 1(具体是0和1和i有关)
这样就化简了求模的过程,共经过i步求模获得当前的目标数k % n = 0(但此时并不知道k具体的值),只知道求mod的过程
要通过操作数获取k,我是这样理解的:
经过i步得到满足条件的数k(尽管k暂时还不知道), 由于每个数会产生两个分支,即k是由 i / 2 层的数k'递推而来的。 即 k'/2 * 2 + 0或1. 因此要知道k的末位是0还是1只要考虑当前k的奇偶性,因为2 * i / 2一定是偶数,奇偶性就是由后面的1或者0来决定的!如果是奇数,则证明当前k的最后一位是1,否则是0,值得注意的是:k的奇偶性和i的奇偶性恰巧是相同的,由于每个数会产生两个新的数字,可以看成是一棵二叉树, 如图:
结点编号即为获取当前k(括号里的数)所需要的操作步数,可以看到k与编号的奇偶性是恰好相同的,因此利用操作步数i, 循环i%2 与i/ = 2 就可以获取目标数的各个位(注意是从叶子到根得到的各个位置上的数(即先得到后面的位的数),因此输出时需要到过来),
优化后的BFS的代码:
/* POJ 1426 Find The Multiple BFS 优化*/ #include <cstdio> int mod[524285]; //寸模,参考别人的计算这是最小的AC下限 int main() { int n; while (scanf("%d", &n) == 1 && n){ mod[1] = 1; int i; for (i = 2; mod[i - 1] != 0; ++i){ //该公式前面已证明, 注意:i为奇偶数分别对应 i%2 = 1和0 mod[i] = (mod[i/2] * 10 + i % 2) % n; // i/2为前一层 } --i; //i此时为取模的次数 int p = 0; while (i){ mod[p++] = i % 2; //操作次数转化成所要的数--->k(可用二叉树理解) i /= 2; } while (p){ printf("%d", mod[--p]); //逆序输出即为目的数 } printf(" "); } return 0; }
解法三: 这题还可以用DFS通过控制递归深度直接过
/* POJ 1426 Find The Multiple --- DFS */ #include <cstdio> int n; bool flag; void dfs(unsigned long long tmp, int i){ if (flag || i > 19) return; if (tmp % n == 0){ flag = 1; printf("%llu ", tmp); return ; } else{ dfs(tmp * 10, i+1); dfs(tmp * 10 + 1, i+1); } } int main() { while (scanf("%d", &n) == 1 && n){ flag = 0; dfs(1,0); } return 0; }