【编程之美】找符合条件的整数

  任意给定一个正整数N,求一个最小的正整数M(M > 1),使得N*M的十进制表示形式里只含有1和0。

  看了题目要求之后,我们首先想到从小到大枚举M的取值,然后再计算N*M,最后判断它们的乘积是否只含有1和0。大体思路可以用下面的伪代码实现:

1 for (M = 2; ; M++)
2 {
3     product = N * M;
4     if (hasOnlyOneAndZero(product))
5         output N, M, product, and return;
6 }

  但问题很快就出现了,什么时候应该终止循环呢?这个循环会终止吗?即使能终止,也许这个循环仍须要耗费太多的时间,比如N = 99时,M = 1122334455667789,N * M = 111111111111111111。

分析与解法

  题目中直接做法显然不是一个令人满意的方法。还有没有其他的方法呢?答案是肯定的。

方法一:转化后枚举

  可以对问题做一个转化:求一个最小的正整数X,X的十进制表示中只有0和1,而且X%N=0。

  我们可以发现,只包含0和1的数X是这样的:1,10,11,100,101,110,111,1000,1001,1010,1011,1100,1101,1110,1111,10000……

  对X循环,依次检查是否能被N整除,如果可以,就找到了最小的X,然后我们可以通过X求出M。

  不难发现,如果最终的X有K位,那么我们的算法时间复杂度至少是O(2K),还能不能改进呢?

方法二:类动态规划

  引入变量J=X%N,用数组bigint[J]表示除N余数为J的最小的正整数X,则bigint[0]即是我们所求。

  对于任意整数X=10i+K,我们有J=X%N=(10i+K)%N=(10i%N+K%N)%N。

  那么,如果bigint[J]还没有计算出来,我们可以用如下方法算得:

    bigint[J]=10i+bigint[K%N]

  如果bigint[J]已经存在的话,我们就不更新它的值。

  为了方便,我们用一个可变长数组来表示bigint[J],如下代码中使用vector<int>来表示。

  1001表示为100+103,即bigint[J]={0, 3}。

  参考代码如下所示:

 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4
 5 typedef long long LL;
 6
 7 const int maxn = 1000007;
 8 vector<int> bigint[maxn];
 9
10 LL tenPow(int n)
11 {
12     LL ret = 1;
13     for (int i = 0; i < n; i++)
14         ret *= 10;
15     return ret;
16 }
17
18 LL bigintToLL(vector<int> bigint)
19 {
20     int len = bigint.size();
21     LL ret = 0;
22     for (int i = 0; i < len; i++)
23         ret += tenPow(bigint[i]);
24     return ret;
25 }
26
27 void solve(int N)
28 {
29     for (int i = 0; i < N; i++)
30         bigint[i].clear();
31
32     bigint[1].push_back(0);
33
34     for (int i = 1, j = 10 % N; ; i++, j = (j * 10) % N)
35     {
36         bool flag = false;
37         if (bigint[j].size() == 0)
38         {
39             flag = true;
40             bigint[j].push_back(i);
41         }
42
43         for (int k = 1; k < N; k++)
44         {
45             int r = (k + j) % N;
46             if ((bigint[k].size() > 0)
47                 && (i > bigint[k][bigint[k].size()-1])
48                 && (bigint[r].size() == 0))
49             {
50                 flag = true;
51                 bigint[r] = bigint[k];
52                 bigint[r].push_back(i);
53             }
54         }
55         if (bigint[0].size() > 0)
56         {
57             break;
58         }
59     }
60 }
61
62 int main(int argc, char *argv[])
63 {
64     int N;
65     while (cin >> N)
66     {
67         solve(N);
68
69         if (bigint[0].size() == 0)
70         {
71             cout << "M not exist" << endl;
72             break;
73         }
74         else
75         {
76             cout << N << " * " << bigintToLL(bigint[0]) / N
77                  << " = " << bigintToLL(bigint[0]) << endl;
78         }
79     }
80 }

扩展问题

1. 对于任意的N,一定存在M,使得N*M的乘积的十制表示只有0和1吗?

结论:对于任意的N,一定存在M,使得N*M的乘积的十进制表示只有0和1。

证明:

  100modN, 101modN, 102modN, ... , 10imodN, ...是一个无限的序列;

  而且,这个序列只能取值0, 1, ... , N-1

  因此,该序列一定是循环的序列,设周期为t,则有

  10smodN = 10s+tmodN = ... = 10s+(N-1)tmodN

  因此:(10s + 10s+t + ... + 10s+(N-1)t)modN = 0

  所以,存在M使得 M * N = 10s + 10s+t + ... + 10s+(N-1)t 而且,这样的M并不唯一。

2. 怎样找出满足题目要求的N和M,使得N*M<216,且N+M最大?

分析:

  首先,我们发现N*M < 216 = 32768,

  则最大的X为11111,其次为11110

  刚开始我认为,1*11111即为满足条件的N和M,最大N+M=11112,

  事实上,这是不正确的,因为当N=1时,最小的M=10即可满足N*M的结果的十进制表示中只有0和1,而当取N=11111时,最小的M=10,而不是1,因为题目要求的M是大于1的,因此,这也是不满足的。

  我们发现,当N越大,M越小时,满足条件的N+M的值可以更大。

  最小的M=2,其次为3

  当M=2时,最大的N=5555,满足N*M=11110,N+M=5557

  当M=3时,最大的N=3700,满足N*M=11100,N+M=3703

  显然,当M越大的时候,最大的N的取值会更小,得到的和也会比较小。

  综上可知,当N=5555,M=2时,满足N*M=11110,此时的N+M最大为5557。

时间: 2024-11-05 10:18:29

【编程之美】找符合条件的整数的相关文章

编程之美---找符合条件的整数

题目:任意给定一个正整数N,求一个最小的正整数M(M>1),使得N*M的十进制表示形式里只含有1和0. 解法:原问题转化为求一个最小的正整数X,使得X的十进制表示形式里只含有1和0,并且X被N整除.于是乎就成了遍历二进制整数一样遍历X的各个取值,但是如果X的最终结果又K位,则要循环搜索2K 次.因此,可以建立一个长度为N的“余数信息数组”,这个数组的第i位保留已经出现的最小的模N为i的X.用BigInt[i]可能很大,只须记下1的位置即可. 1 for(i=0;i<N;i++) 2 BigIn

第2章 数字之魅——找符合条件的整数

找符合条件的整数 问题描述 任意给定一个正整数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都为

【编程之美】2.8 找符合条件的整数

给定一个正整数N,求一个最小的正整数M(M > 1),使得N * M的十进制表示中只有0和1. 我的思路: 从最低位到最高位找M,每次使得乘积的最后面多一位符合0.1的条件. 那么先找能够让末尾数字变成0的备选项 举例若N的个位数是9  考虑从后面来的进位 c 让 x * 9 + c 的末尾是0或1 设个位数字为9 则eligibleNum中存储的数字eligibleNum[0][0] = 0 因为9 * 0 + 0 = 0 末尾符合0或1 eligibleNum[0][0] = 9 因为9 *

编程之美2.8 | 找符合条件的整数

思路还是相当地巧妙. 求余数的话,(a+b)%n=(a%n+b%n)%n; 用vector来表示整数的话(出现1的位置),可以避免溢出. 注意第20行,在更新remainders[(j+r)%n]时,要确保每个remainders的每个序列都是递增的,不能存在相等的情况. 1 #include <time.h> 2 #include <math.h> 3 #include <stdlib.h> 4 5 using namespace std; 6 7 long long

编程之美2.8——找符合条件的整数

任意给定一个正整数N,求一个最小的正整数M(M>1),使得N*M的十进制表示形式里只含有1和0. 如N=3,M=39,N*M=111. [思路] 这么难的思路打死我也想不到[email protected][email protected]|||||.. 将题目转换为,求一个数X,使得X%N=0且X的十进制表示只含有1和0. 维护一个“余数数组”,对于从0到N-1的每一个余数,都有相应的最小X: 高位可以利用低位的余数归队,X=10^k+Y(10的k次方,^表示次方)X%N=(10^k%N+Y%

(转)找符合条件的整数

题目:任意给定一个正整数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(M>1),使得N*M的十进制表示形式只含有1和0 . 分析: 将问题"求一个最小的正整数M,使得N*M的十进制表示形式里只含有1和0"转换为求一个最小的正整数X,使得X的十进制表示形式里只含有1和0,并且X被N整除.

编程之美 找出符合条件的整数

好不容易把内容看懂~ 最主要的一句话:只需要将10k%N的结果与余数信息数组里非空的元素相加,再去模N,看看会不会出现新的余数~ 时间太紧迫~先把自己写的代码贴上,以后再详解 1 int FindMin(int N) 2 { 3 if(N <= 1) 4 return N; 5 6 int* A = new int[N];//这个是记录模N余i之后的数值 7 8 memset(A, -1, sizeof(int) * N); 9 int factor = 1; 10 A[1] = 1; 11 1

编程之美——符合条件的整数

问题:给定整数N,求最小整数M,使得N*M的十进制表示中只含有1和0: 当M很大时,机器可能不能表示M,对问题转化:求以最小整数X,使得X的十进制表示中只含1和0,并且被N整除: 此问题必定有解:可参考:http://blog.csdn.net/spaceyqy/article/details/38337387 代码实现(具体思路参考编程之美或:http://www.cnblogs.com/jfcspring/p/3776388.html): 1 #include<iostream> 2 #i