编程之美-2.8-找符合条件的整数
1. 简述
任意给定一个正整数N,求一个最小的正整数M(M>1),使得N*M的十进制表示形式里只含有1和0。
比如N=99时,M=1 122 334 455 667 789,N * M=111 111 111 111 111 111。
2. 思路
实际上两个思路,一个就是给N,遍历M,判断N*M是否满足条件的数值;另一个就是给N,遍历满足条件的数值,判断数值能否被N整除。根据样例数据发现,N=99时,第一个方法的复杂度为:M=1 122 334 455 667 789,第二个方法的复杂度为2^15=32 * 1024。即由0和1组成的数值实际上是十分稀疏的,从这边遍历更快。这一点与编程之美上面3.2节中的电话号码和英文单词对应很类似,即电话号码的组合是非常巨大的,但是英文单词是很有限的。
在遍历只有0或者只有1的数值的过程中,假设目标结果有k位,那么最多需要遍历2^k个数字,实际上还可以进一步简化,比如对于123,123 = 100 + 23即123%N=100%N + 23%N。假设当前已经计算了个位数和十位数的情况,那么可以保持个位数和十位数对N的那些可能余数,对于百位数对N的余数,必然在(100%N+前面计算过的余数)%N的范围内,如果有新的余数产生,更新余数即可。保存余数的时候,记录该余数的最小数值即可。这样假设目标结果有k位,那么计算复杂度就是k(N-1)。
3. 代码
#include<iostream>
using namespace std;
int find_M(int N) {
// 边界条件
if(N == 1)
return 1;
// 初始化余数数组
int *A = new int[N]; // 记录已有的余数,A[i]表示对N的余数为i的最小满足条件的数值
int *B = new int[N]; // 记录更新的余数
memset(A, -1, N*sizeof(int));
A[1] = 1;
// 寻找过程
int factor = 10;
bool not_found = true;
while(not_found) {
memset(B, -1, N*sizeof(int));
int x = factor % N; // 高位数值对N的余数
// 高位数值 + 0 的情况
if(A[x] == -1) {
B[x] = factor;
if(x == 0)
break;
}
// 高位数值 + 低位正整数的情况
for(int i=1; i<N; i++) { // 遍历每个可能的余数
if(A[i] == -1)
continue;
int new_x = (x + i) % N; // 计算出的余数
if(A[new_x] == -1 && B[new_x] == -1) { // 如果是一个新的余数,保存
B[new_x] = factor + A[i];
if(new_x == 0) { // 刚好找到的新的余数是0
not_found = false;
break;
}// if
}// if
}//for
factor *= 10;
for(int i=0; i<N; i++) {
if(A[i]==-1 && B[i]!=-1) {
A[i] = B[i];
}
}
}// while
int result = B[0];
delete[] A;
delete[] B;
return result;
}
int main() {
int N;
while(true) {
cout << "N:";
cin >> N;
if(N < 1)
break;
cout << "M:" << find_M(N)/N << endl;
cout << "正整数为:" << find_M(N)/N*N << endl;
}
system("PAUSE");
return 0;
}
注意:代码中之所以用了两个余数数组,是为了计算出高位的所有的新的余数,然后一起保存,如果一个一个的计算,那么高位会受到自身的影响。比如:假设当前计算百位,假设101被保存在A[7],当前计算到A[2],那么在计算100+A[7]的时候,实际上算的数值就是100+101=201是非法字符,因此,每次计算出的要一次性的保存下去。另外需要注意,对于0每次单独处理,即10+0,100+0的情况。
结果输出为:
4. 编程之美的代码
上面的代码是看了基本想法写的,然后又看了下书上的代码,发现还是书上的精髓一些,书上代码更优的地方有如下几个:第一,如果N*M数字很大,int存不下的,书上用队列记录数值中1的位置,比如对于1,记录0,对于1110011,记录01456即可。第二,每个循环节,如果没有新的余数出现,说明永远找不到满足条件的M。这个怎么证明,还没太想清楚。此外,书中代码也考虑了对高位+0和高位+低位两种情况,以及高位一次性保存的问题,不过书中代码更优雅一些。
5. 参考
编程之美,2.8节,找符合条件的整数