给定数字A找到最小的数字B,使得A * B仅包含数字4和0,而零只应在最后,即像404这样的数字无效。
例如:
| A | B | A*B |
|---|-----|-----|
| 1 | 4 | 4 |
| 2 | 2 | 4 |
| 3 | 148 | 444 |
| 4 | 1 | 4 |
| 5 | 8 | 40 |
好吧,我以这种方式尝试了这个问题。保持整数队列。最小的数字是4.
Pop the number (i.e. the front element of the queue) and push the numbers that can be derived from this popped number.
That is , when we pop 4, we push (4*10) = 40 and (4*10 + 4) = 44 with the constraint that the popped number is not divisible by 10. If it is, push only its next multiple of 10.
所以,我的队列将是:4,40,44,400,440,444,......
当我从队列中弹出元素时,我将检查它是否可以被给定的数字A整除。如果是,只需中断并弹出数字就是我想要的结果。
因为我的号码真的很大,所以我保留了pair<string,int>
的队列,其中string对应number
,int对应remainder
。因此,可以很容易地计算下一阶段的剩余部分。
前:
queue : <"4",4>
Pop , current result is string : "4" and remainder is lets say prev = 4
check if the last digit is 0 or not (for checking if its a multiple of 10 or not)
If not, then append 0 to this string and remainder as (prev*10)%a and push this pair in the queue. Also, append 4 to this string with remainder as : (prev*10 +4)%a and push. If the last digits is 0, append 0(not 4) and corresponding remainder, push this pair in the queue.
Queue: <"40",(prev*10)%a>, <"44", (prev*10 +4)%a> and so on..
直到队列前面的对为余数0,我们将继续这样做。
虽然这种改进似乎有点好,但不正确并没有通过所有测试用例。有人可以说明如何以最佳方式解决这个问题。 (A的范围是10 ^ 10)。
答案 0 :(得分:11)
如果我理解了问题,解决方案必须与regualr表达式0 | 4 + 0 *
匹配它花了很多时间我不用C编程,但是下面的代码可以解决问题。
int calc( int n ) {
int factor5;
int factor2;
int j;
int a;
int b;
int i;
/* trivial case 0 result is 0 */
if( n==0 ) return 0;
/* find the number of factors 2 and 5 in n */
for( a=n, factor5=0; a%5==0; a/=5, factor5++ );
for( a=n, factor2=0; a%2==0; a/=2, factor2++ );
/* result is r=b*a where a=2^(j+2)*5^j */
j=factor5;
if( j < factor2-2 ) j=factor2-2;
for( a=4,i=0; i<j; i++, a*=10 );
/* generate b in 1, 11, 111, ... until solution found */
for( b=1; (a*b)%n!=0; b=10*b+1 );
return a*b;
}
可以通过以下方式进行测试:
int main ( void ) {
int n,r;
for( n=0; n<10; n++) {
r=calc(n); printf( "n=%d r=%d\n", n, r );
}
return 0;
}
注意:优化它。 Alsochange“int”to“long”,“long long”或使用任意长度的图书管理员。
试验:
n=0 r=0
n=1 r=4
n=2 r=4
n=3 r=444
n=4 r=4
n=5 r=40
n=6 r=444
n=7 r=444444
n=8 r=40
n=9 r=444444444
<强> Rationalle:强>
除了具有结果0的普通情况0之外,结果“r”必须匹配正则表达式“4 + 0 *”:fours后跟零。也就是说,在正常的算术中,r = x * 10 ^ j,其中x在4,44,444,....
如果我们提取因子4,我们在序列1,11,111,...中使用x得到r = x * 4 * 10 ^ j。注意,这个序列中的数字从不是因子2和5(它们不是偶数,也不是0或5)。
总之,r = x * 2 ^(j + 2)* 5 ^ j,其中x为1,11,111,...和“j”,从参数的因式分解中获得。程序的第一步是计算“j”,接下来计算a = 2 ^(j + 2)* 5 ^ j,最后生成序列1,11,111,...并测试它直到第一个有效结果。
答案 1 :(得分:4)
让我们拨打40个号码C
,即C = A x B
。
根据我理解的限制条件,这会使语言C = AxB
中的4^n 0^m
(请勿将此作为4
读取为权力n
,这是为了表示重复4
n
次),因此我们只需要n
和m
来描述C
。
<强>观察:强>
B
相当于找到C
倍数的最小A
。C
- 数字的位数不同,则位数较高的C
会更大。C
- 数字的位数相等n + m
时,C
的{{1}}数越大越好。因此,我们循环使用4
(以C
开头并增加)和1
中4
的数字位数来固定数字数字,再次从单个C
开始并增加。这为数字顺序提供了所有可能的4
- 数字。
如前所述并在@pasaba por aqui的回答中使用,可以通过利用C
和A
可能共享素数因子的事实来减少搜索空间。在这种情况下,每个C
始终至少有素因子C
(2
),可能还有2^2 = 4
(5
)。
确实,2*5 = 10
与C = x * 2^(j + 2) * 5^j
(即x in [1, 11, 111, ...]
)。最小C = x * 4 * 10^j
等于j
中2
或5
个因素的最高数量。例如。如果A
,A % 25 == 0
需要j
,2
,A % 400 == 0
需要j
,依此类推。
请参阅pasaba por aqui对该解决方案的回答。
蛮力版本:
4
答案 2 :(得分:4)
这里我给出了一个有效的算法,即在A的值中及时工作(编辑:校正)多项式以解决数字A的问题。我还给出了一个证据,证明所有数字A和证明我的算法的正确性 - 证据表明,对于任何数字A,正确的答案最多有A ^ 2 4(并且零的数量最多是A的数字的两倍,粗略地) 。 (我不知道A ^ 2四肢是否是最好的界限,但我认为它可能并不太远。)运行时间不会超过A或输出的多项式。 (我没有完全解决。)现在看到其他答案,它与pasaba por aqui的答案基本相同,但我想我会更加严谨地解释它为何起作用。 (虽然我的写作可能会得到改善......)
术语:在续集中,我将说a number N has the form {STRING}
表示它的十进制扩展与正则表达式{STRING}匹配,即使这不是标准的数论术语。
问题:给定A,找到形式为“4 + 0 *”的最小整数N,使得N mod A = 0
步骤1:考虑10 mod A,特别是n = 1,2,3的序列{10 ^ n mod A},...
第一个显而易见的问题是,如果10是可逆的模式A,即10与A相互作用会发生什么,如果不相反则相反。 (编辑:这实际上并不明显,但在这些基本数论的90%中,取得进展的方法是根据所涉及的数字的主要因素分析进行一些案例分析,并思考何时事物是互质的,何时是他们共同的共同因素往往是一个很好的方向。)
如果10与A不相互作用,则有一些可能性。一个是10分A,这是一个愚蠢的案例。我们可以简单地将A除以10,然后找到答案,然后将其乘以10.如果排除了,那么5除以A,但是2除以A,或2除以A但是5除以A,或者A与10相除
假设5除以A,但2则不对。如果N mod A = 0具有上述形式,则考虑N mod 5 - 它等于自5 |以来的最低位数因此,最低位数必须为0而不是4,所以10 | N.也就是说,在这种情况下,形式为“4 + 0 *”的任何整数,使得N mod A = 0,也具有N mod 2A = 0.并且10除以2A,这意味着我们可以减少到更简单的问题
假设2除以A,但5则不对。很明显,4实际上除了任何数量的形式“4 + 0 *”,因此对于任何奇数A',所描述的最小整数N是相同的,无论我们将A取为A',2A'还是4A ”。现在假设8除以A.由于8除以任何数字的形式“40+”,而8不划分4,通过与之前类似的参数暗示数字N必须具有零作为其低位,所以如果8 | A,它意味着如果N mod A = 0,那么N mod 5A = 0.所以我们可以移动到这个数字,然后取出10的幂并减少到一个更简单的问题。
因此,我们可以将注意力限制在10与A相互作用的情况。
这简化了事情因为那时基本数论(中国余数定理)告诉我们10是可逆的模态A,即对于一些足够大的k,10 ^ k = 1 mod A.这也意味着我们可以忽略N的数字中零的可能性 - 因为如果X * 10 ^ y = 0 mod A,而10是可逆的mod A,我们还必须具有X = 0 mod A,这将是是一个较小的解决方案。
因此,一旦10与A相互作用,则形式为“4 + 0 *”的最小整数N使得N mod A = 0与形式“4+”的最小整数相同,使得N mod A = 0。(另外,它现在很清楚,总是存在这种形式的SOME整数N可被A整除。所以所有这些程序确实终止并且不为任何输入无限循环:)因为,我们可以做到双赢分析。假设10 ^ k = 1 mod A.现在考虑由正好k 4组成的十进制数K的值,减去模数A.如果这是零,那么证明数字存在并且我们已经完成了。如果它不为零,则说它是某个值“a”mod A不等于0.我们知道数字K * 10 ^ k也等于“a”mod A,因为10 ^ k = 1 mod A.并且K * 10 ^ k也具有我们关心的形式,对于任何i,K * 10 ^ {ik}也是如此。因此,如果我们取一个由A * k 4组成的十进制数,它必须等于A * a = 0 mod A.因此,我们构造了一个可以被A整除的所需形式的数字N.)
现在我们可以通过一个简单的for循环直接解决这个问题。我们只是跟踪值“4000000 ... mod A”和值“444444 .... mod A”,其中数字是k位长,我们计算k + 1位数的模数值,将第一个值乘以10 mod A的值,减去模A,然后将其加到第二个并减少模数A.
这是完整的代码:
#include <cassert>
#include <iostream>
typedef unsigned long long ul;
ul fast_finder(ul A) {
assert(A);
ul num_zeros = 0; // remember how many zeros we need to add at the end
while ((A % 10) == 0) {
A /= 10;
++num_zeros;
}
while ((A % 5) == 0) {
A /= 5;
++num_zeros;
}
while ((A % 8) == 0) {
A /= 2;
++num_zeros;
}
while ((A % 2) == 0) {
A /= 2;
}
ul four_mod_A = 4 % A;
ul ten_mod_A = 10 % A;
ul num_fours = 1;
// in these variable names "k" is the number of fours we are considering
ul four_times_ten_to_the_k_mod_A = four_mod_A;
ul sum_of_fours_mod_A = four_mod_A;
while (sum_of_fours_mod_A) {
four_times_ten_to_the_k_mod_A *= 10;
four_times_ten_to_the_k_mod_A %= A;
sum_of_fours_mod_A += four_times_ten_to_the_k_mod_A;
sum_of_fours_mod_A %= A;
++num_fours;
}
// now build an integer representation of the result from num_fours, num_zeros
ul result = 0;
while (num_fours) {
result *= 10;
result += 4;
--num_fours;
}
while (num_zeros) {
result *= 10;
--num_zeros;
}
return result;
}
// This is to check the correctness of the fast algorithm, it's just the naive algorithm.
ul slow_finder(ul A) {
assert(A);
for (ul B = 1;;++B) {
ul N = B * A;
bool saw_four = false;
while (N) {
ul low = N % 10;
if (low == 4) {
saw_four = true;
} else if (low != 0 || saw_four) { break; }
N /= 10;
}
if (N == 0)
return A*B;
}
}
void check(ul x) {
std::cout << x << ": ";
ul f = fast_finder(x);
std::cout << f << std::flush;
ul s = slow_finder(x);
if (f != s) {
std::cout << "failed! ( " << s << " )" << std::endl; return;
}
std::cout << '.' << std::endl;
}
int main() {
check(1);
check(3);
check(4);
check(5);
check(10);
check(11);
check(13);
check(15);
check(18);
check(73);
check(64);
check(52);
}