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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编程之美找符合条件的整数相关的知识,希望对你有一定的参考价值。

  任意给定一个正整数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 }
View Code

扩展问题

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。

 

以上是关于编程之美找符合条件的整数的主要内容,如果未能解决你的问题,请参考以下文章

编程之美子数组的最大乘积

编程之美寻找数组中的最大值和最小值

编程之美数组分割

编程之美求数组的子数组之和的最大值

《编程之美》“1的数目”的另一个解法

编程之美 2.10寻找数组中的最大最小值