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

How to count numbers between 2 numbers L and R (both included) such that product of the digits of the selected numbers is even?

本文关键字:数字 之间 包括 计算 何计算      更新时间:2023-10-16

如何找到 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 的偶数产品编号的计数这就是我的想法..?这能解决问题吗?

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

(对于格式,我深表歉意;我希望内容仍然足够清晰。

首先,让我们简化问题:

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 是一个位数(即 <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 = (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

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