问题描述:
给一个整数N,求一个整数M,使M*N 的十进制结果中只有 1 和 0。
问题分析:
问题从结果入手,十进制表示只有 1 和 0,这个数字的集合有规律,例如: 1,10,11,100,101,110,111….. 10k次方与前面的每个的数的和。 例如 k=1 ==> 10 ,在 10这个阶段的数字有 1 + 10 = 11 例如 k=2 ==> 100 ,在 100这个阶段的数字有 1 + 100 = 101, 10+100=110,11+100=111
方式1: 从1开始,尝试每一个1,0组合的结果Result,找到 Result % N == 0的Result,再用 Result / N就得到M。 可以做到,但效率不高。
方式2:每个数字的遍历会比较浪费时间,书中用余数的方式进行考虑。 假设最后的乘积为X,X有可能是10的k次方,也有可能是10的k次方与有效数集合中之前一个数的和, 假设
X = 10^k + Y,Y也满足只有0和1组成,类似于 111 = 100 + 11。如果X是期望的结果,那么 一定有(10^k % N + Y % N) == N
可以定义一个大小为N的数组(X%N的余数的范围是从1~N-1),用来保存X的值的余数。 如果数组中两个数组下标(这里的数组下标就是余数)的和为N,并且两个元素都有效,那最后的结果就是这两个元素的和。
用数字3举例
N = 3, 余数数组 mod_list[3+1] 定义有 3+1个元素,方便计算
k = 0 X = 1 mod 3 = 1 mod_list[1] = 1 k = 1 X = 10 mod 3 = 1 因为 mod_list[1] = 1 已经存在了,所以不用改变。 推导出 mod_list[1+1] = 11 即 11%3=2 mod_list[2] = 11 k = 2 X = 100 mod 3 = 1,这时 mod_list[2] 已经存在了,而且 2+1=3 那最后的结果就是 100 + mod_list[2] = 100 + 11 = 111
这种方式是 3次取模,一次加法运算。
如果用遍历的方式则需要遍历(1, 10, 11, 100, 101, 110, 111) 7个数字,7次取模。2^k – 1次
#include <iostream>
#include <vector>
using namespace std;
int find2(int num)
{
int result = 0, k = 0, step = 1;
int i = 0, j = 0, curr = 0;
bool exist = false;
int* plist = new int[num+1];
vector<int> list;
memset(plist, 0, sizeof(int)*(num+1));
while (result == 0)
{
k = step % num;
if (k == 0)
{
result = step;
}
else if (plist[num-k] != 0)
{
result = plist[num-k] + step;
}
else
{
list.clear();
if (plist[k] == 0)
{
list.push_back(k);
plist[k] = step;
}
exist = false;
for (i = 1; i < num+1; i++)
{
curr = (i+k) % num;
for (j = 0; j < list.size(); j++)
{
if (list[j] == i)
exist = true; // make sure the same value not calculate twice
}
if ((plist[i] != 0) && (!exist) && (plist[curr] == 0))
{
list.push_back(curr);
plist[curr] = step + plist[i];
}
}
}
step *= 10;
}
cout << num << " * " << result/num << " = " << result << endl << endl;
delete[] plist;
plist = NULL;
return result;
}
int find(int num)
{
int result = 0;
int step = 10, i = 0, curr = 0, cnt = 0;
vector<int> list;
list.push_back(1);
while(list[curr]%num != 0)
{
if (curr == list.size()-1)
{
list.push_back(step);
cnt = list.size() - 1;
for (i = 0; i < cnt; i++)
list.push_back(list[i]+step);
step *= 10;
}
curr++;
}
result = list[curr];
cout << num << " * " << list[curr]/num << " = " << list[curr] << endl << endl;
return result;
}
void main()
{
int test[] = {5, 12, 11, 6, 9, 21, 30};
int len = sizeof(test)/sizeof(test[0]);
int i = 0;
for (i = 0; i < len; i++)
find(test[i]);
cout << "\n\n=======================================\n" <<endl;
for (i = 0; i < len; i++)
find2(test[i]);
cin >> i;
}