《编程之美》读书笔记(十三):“找符合条件的整数”

题目:任意给定一个正整数N,求一个最小的正整数M(M>1),使得N*M的十进制表示形式里只含有1和0.
解决这个问题首先考虑对于任意的N,是否这样的M一定存在。可以证明,M是一定存在的,而且不唯一。
简单证明:因为

 

这是一个无穷数列,但是数列中的每一项取值范围都在[0, N-1]之间。所以这个无穷数列中间必定存在循环节。即假设有s,t均是正整数,且s<t,有 。于是循环节长度为t-s。于是10^s = 10^t。因此有:
,所以

例如,取N=3,因为10的任何非负次方模3都为1,所以循环节周期为1.有:

给定N,求M的方法:
方法一:给定N,令M从2开始,枚举M的值直到遇到一个M使得N*M的十进制表示中只有1和0.
方法二:求出10的次方序列模N的余数序列并找出循环节。然后搜索这个余数序列,搜索的目的就是要在这个余数序列中找到一些数出来让它们的和是N的倍数。例如N=13,这个序列就是1,10,9,12,3,4然后不断循环。很明显有1+12=13,而1是10的0次方,12是10的3次方,所以这个数就是1000+1=1001,M就是1001/13=77。
方法三:因为N*M的取值就是1,10,11,100,101,110,111,......所以直接在这个空间搜索,这是对方法一的改进。搜索这个序列直到找到一个能被N整除的数,它就是N*M,然后可计算出M。例如N=3时,搜索树如下:

上图中括号内表示模3的余数。括号外表示被搜索的数。左子树表示0,右子树表示1.上图中搜索到第二层(根是第0层)时遇到111,它模3余数为0.所以N*M=111, M=111/3=37。
方法四:对方法三的改进。将方法三的搜索空间按模N余数分类,使得搜索时间和空间都由原来的指数级降到了O(N)。改进的原理:假设当前正在搜索由0,1组成的K位十进制数,这样的K位十进制数共有2^k个。假设其中有两个数X、Y,它们模N同余,那么在搜索由0、1组成的K+1位十进制数时,X和Y会被扩展出四个数:10X, 10X+1, 10Y, 10Y+1。因为X和Y同余(同余完全可以看作相等),所以10X与10Y同余,10X+1与10Y+1同余。也就是说由Y扩展出来的子树和由X扩展产生出来的子树产生完全相同的余数,如果X比Y小,那么Y肯定不是满足要求的最小的数,所以Y这棵子树可以被剪掉。这样,2^K个数按照模N余数分类,每类中只保留最小的那个数以供扩展。原来在这一层需要搜索2^K个数,现在只需要搜索O(N)个数。例如,当N=9时,第0层是1(1),

如上图所示,第2层的110,第三层的1010、1110都因为同一层有和它同余且更小的数而被剪掉。如果按照方法三搜索,第三层本来应该有8个结点,但现在只有4个结点。

 

方法一的源代码:

  1. #include <stdio.h>   
  2.   
  3. int HasOnlyOneAndZero(unsigned int n) {   
  4.     while(n) {   
  5.         if(n % 10 >= 2) return 0;   
  6.         n /= 10;   
  7.     }   
  8.     return 1;   
  9. }   
  10.   
  11. int main() {   
  12.     int n, m;   
  13.     while(scanf("%d", &n) != EOF) {   
  14.         for(m = 1;;m++) {   
  15.             if(HasOnlyOneAndZero(n*m)) {   
  16.                 printf("n = %d, m = %d, n*m = %d\n", n, m, n*m);   
  17.                 break;   
  18.             }   
  19.         }   
  20.     }   
  21.     return 0;   
  22. }  

方法三的源代码:

  1. // 解法三(1):广度优先搜索   
  2. #define _CRT_SECURE_NO_WARNINGS 1   
  3.   
  4. #include <cstdio>   
  5. #include <queue>   
  6. using namespace std;   
  7.   
  8. int main() {   
  9.     int N;   
  10.     while(scanf("%d", &N) != EOF) {   
  11.         queue<int> q;   
  12.         q.push(1);   
  13.         while(!q.empty()) {   
  14.             int t = q.front();   
  15.             q.pop();   
  16.             if(t % N == 0) {   
  17.                 printf("n = %d, m = %d, n*m = %d\n", N, t/N, t);   
  18.                 break;   
  19.             }   
  20.             q.push(t * 10);   
  21.             q.push(t * 10 + 1);   
  22.         }   
  23.     }   
  24.     return 0;   
  25. }  

方法四源代码:

  1. // 解法四:将搜索空间分过类的广度搜索,这样空间占用是O(N)而不是   
  2. // 指数级。分类的原则是按照模N的余数分类    
  3. #define _CRT_SECURE_NO_WARNINGS 1   
  4.   
  5. #include <cstdio>   
  6. #include <bitset>   
  7. #include <vector>   
  8. #include <queue>   
  9. using namespace std;   
  10.   
  11. struct QNode {   
  12.     int v, r; // v is value, r is remainder   
  13.     QNode(int vv, int rr): v(vv), r(rr) {}   
  14.     QNode(): v(0), r(0) {}   
  15. };   
  16.   
  17. int main() {   
  18.     int N;   
  19.     while(scanf("%d", &N) != EOF) {   
  20.         //bitset<N> bn;   
  21.         queue<QNode> q;   
  22.         q.push(QNode(1, 1));   
  23.         while(!q.empty()) {   
  24.             //bn.reset();   
  25.             vector<bool> bn(N, false);   
  26.             int s = q.size();   
  27.             while(s--) {   
  28.                 QNode t = q.front();   
  29.                 if(t.r == 0) {   
  30.                     printf("n = %d, m = %d, n*m = %d\n", N, t.v/N, t.v);   
  31.                     goto ok;   
  32.                 }   
  33.                 q.pop();   
  34.                 if(!bn[t.r * 10 % N]) {   
  35.                     bn[t.r * 10 % N] = true;   
  36.                     q.push(QNode(t.v * 10, t.r * 10 % N));   
  37.                 }   
  38.                 if(!bn[(t.r * 10 + 1) % N]) {   
  39.                     bn[(t.r * 10 + 1) % N] = true;   
  40.                     q.push(QNode(t.v * 10 + 1, (t.r * 10 + 1) % N));   
  41.                 }   
  42.             }   
  43.         }   
  44. ok:;   
  45.     }   
  46.     return 0;   
  47. }  

原文链接:http://blog.csdn.net/jcwKyl/archive/2009/02/02/3859155.aspx

posted @ 2009-02-06 17:16  博文视点  阅读(2493)  评论(1编辑  收藏  举报