如何计算2个数字L和R(均包括在内)之间的数字,使得所选数字的数字乘积是偶数?

时间:2013-01-24 17:48:52

标签: c++ algorithm

如何找到2个数字L和R(均包括在内)的数字计数,它们的数字乘积是偶数? 除了蛮力外,我们怎么办呢?

dp[0][0]=4;
dp[0][1]=5;
for(int l=1;l<=9;l++)
{
    dp[l][0]=0;
    dp[l][1]=0;
    dp[l][0]+=(dp[l-1][0])*10;
    dp[l][0]+=dp[l-1][1]*5;
    dp[l][1]+=dp[l-1][1]*5;          
}

这是我制作的蛮力检查员,我正在努力开发更有效的解决方案

bool f(ll n)
{
  ll p=1;
  if(n==0)
  return true;
  while(n)
  {
     p*=n%10;
     n/=10;
     if(p%2==0) return true;
     p=1;
  }
  if(p%2) return false;
  else
  return true;
}
ll brute(ll l,ll r)
{
   if(l>r) swap(l,r);
    ll cnt=0;
   for(ll  i=l;i<=r;i++)
   {
      if(f(i))
      {
         cnt++;
      }
   }

return cnt;

}

dp[l-1][0]存储长度为l的偶数乘积数 这就是我的想法..? 这可以解决问题 ?

2 个答案:

答案 0 :(得分:3)

蛮力是一种非常浪费的方法。我们可以做得更好。

(我为格式化道歉;我希望内容仍然足够清晰。)

首先,让我们简化问题:

EvenProductNumbersBetween(RangeStart, RangeEnd) = NumbersBetween(RangeEnd - RangeStart) - AllOddDigitNumbersBetween(RangeStart, RangeEnd)

NumbersBetween(RangeStart, RangeEnd) = (RangeEnd - RangeStart) + 1

AllOddDigitNumbersBetween(RangeStart, RangeEnd) = AllOddDigitNumbersUpTo(RangeEnd) - AllOddDigitNumbersUpTo(RangeStart-1)

现在我们得到了肉:计算AllOddDigitNumbersUpTo(RangeEnd)

首先,考虑一下这些简单的案例:

(假设RangeEnd为正)

如果RangeEnd是单个数字(即<10),那么

AllOddDigitNumbersUpTo(RangeEnd) = Floor((RangeEnd+1)/2)

E.g.:
AllOddDigitNumbersUpTo(0) = {} = 0
AllOddDigitNumbersUpTo(1) = {1} = 1
AllOddDigitNumbersUpTo(2) = {1} = 1
AllOddDigitNumbersUpTo(3) = {1,3} = 2
AllOddDigitNumbersUpTo(4) = {1,3} = 2
AllOddDigitNumbersUpTo(5) = {1,3,5} = 3
AllOddDigitNumbersUpTo(6) = {1,3,5} = 3
AllOddDigitNumbersUpTo(7) = {1,3,5,7} = 4
AllOddDigitNumbersUpTo(8) = {1,3,5,7} = 4
AllOddDigitNumbersUpTo(9) = {1,3,5,7,9} = 5

如果RangeEnd可以是具有特定位数的任何数字,那么

考虑每个数字必须具有五个奇数中的一个作为选择(前导零缩短长度,因此被排除),因此直接计算这是微不足道的:

AllOddDigitNumbersOfLength(NumberLength) = 5^NumberLength

E.g.:
AllOddDigitNumbersOfLength(1) = {1, 3, 5, 7, 9} = 5
AllOddDigitNumbersOfLength(2) = {1, 3, 5, 7, 9} * {1, 3, 5, 7, 9} = 5*5 = 25
AllOddDigitNumbersOfLength(3) = 5*5*5 = 125
...

否则,将RangeEnd分开:

RangeEnd = (FirstDigit * 10^PowerOfFirstDigit) + Remainder

AllOddDigitNumbersUpTo(RangeEnd) = AllOddDigitNumbersUpTo(FirstDigit) * AllOddDigitNumbersOfLength(PowerOfFirstDigit-1) + AllOddDigitNumbersUpTo(Remainder)

不幸的是,有一个前导零的复杂情况。 (感谢@AndyProwl用我的答案的早期版本指出我的问题!)如果Remainder以零开头,那么我们不应添加AllOddDigitNumbersUpTo(Remainder)术语,最后,因为受约束的前导零会使产品即使是我们试图制作的每个较小的数字。

E.g.:

AllOddDigitNumbersUpTo(6300193) =
= AllOddDigitNumbersUpTo(6*(10^7) + 300193)
= AllOddDigitNumbersUpTo(6) * AllOddDigitNumbersOfLength(7-1) + AllOddDigitNumbersUpTo(300193)
= 3 * 5^6 + AllOddDigitNumbersUpTo(300193)
= Trivial * Trivial + LogarithmicallySmallerCase

AllOddDigitNumbersUpTo(300193) =
= AllOddDigitNumbersUpTo(3*(10^6) + 00193)
= AllOddDigitNumbersUpTo(3) * AllOddDigitNumbersOfLength(6-1)
= 2 * 5^5
= Trivial * Trivial

答案 1 :(得分:0)

因为您可能必须检查每个数字,所以最小时间是线性O(n)所以我会蛮力,因为我认为没有算法。