問題描述:
給一個整數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;
}