📜  [L,R]范围内的整数数,可以被整数K整除

📅  最后修改于: 2021-04-22 06:59:54             🧑  作者: Mango

给定一个值范围[L,R]和一个值K ,任务是计算给定范围内的数字,这些数字至少可以被该数字的十进制表示形式中存在的K个数字整除。

例子:

方法1:天真的方法

  • 对于介于LR之间的任何数字,请找到将其除以数字的位数。
  • 如果以上步骤中的数字计数大于或等于K ,则将该数字包括到最终计数中。
  • 对从L到R的所有数字重复上述步骤,并打印最终计数。

时间复杂度: O(N),其中N是范围[L,R]之间的差。

方法2:高效方法
我们将使用Digit DP的概念来解决此问题。以下是解决此问题的观察结果:

  • 对于所有正整数(例如a ),要找到数字从2到9的可除数,可以按如下所述减少数字a ,以有效地找到可除数:
a = k*LCM(2, 3, 4, ..., 9) + q
where k is integer and
q lies between range [0, lcm(2, 3, ..9)]
LCM(2, 3, 4, ..., 9) = 23x32x5x7 = 2520
  • 在执行a =模2520之后,我们可以从将该模除的原始数a中找到位数。

以下是执行此操作的步骤:

  1. 存储给定范围内的所有数字,并以降序对数字进行排序。
  2. 遍历上面存储的所有数字并生成严格小于给定数字范围的所有数字。
  3. 要生成小于给定数字的数字,请使用紧密变量,例如:
    • Tight的值为0,表示包含该数字将使数字小于给定范围。
    • Tight的值为1,表示通过包含该数字,它将给出大于给定范围的数字。因此,我们可以在获得紧的值1之后删除所有排列,以避免更多的递归调用。
  4. 生成所有数字排列之后,找到除以该数字的位数大于或等于K的数字
  5. 将每个排列数字的计数存储在dp表中,以将结果用于子问题重叠。

下面是上述方法的实现:

C++
// C++ program to Find the number
// of numbers in a range that are
// divisible by exactly K of it's
// digits
#include 
using namespace std;
 
const int LCM = 2520;
const int MAXDIG = 10;
 
// To store the results for
// overlapping subproblems
int memo[MAXDIG][2][LCM][(1 << 9) + 5];
 
// To store the digits of the
// given number
vector dig;
int K;
 
// Function to update the dp
// table
int dp(int index, int tight,
       int rem, int mask)
{
 
    // To find the result
    int& res = memo[index][tight][rem][mask];
 
    // Return the if result for the
    // current iteration is calculated
    if (res != -1) {
        return res;
    }
    res = 0;
 
    // If reaches the end of the digits
    if (index == dig.size()) {
        int cnt = 0;
 
        // Count the number of digits
        // that divides the given number
        for (int d = 1; d < 10; d++) {
            if (mask & (1 << (d - 1))) {
                if (rem % d == 0) {
                    cnt++;
                }
            }
        }
 
        // If count is greater than or
        // equals to K, then return 1
        if (cnt >= K) {
            res = 1;
        }
    }
 
    // Generates all possible numbers
    else {
        for (int d = 0; d < 10; d++) {
 
            // If by including the current
            // digits gives the number less
            // than the given number then
            // exclude this iteration
            if (tight & (d > dig[index])) {
                continue;
            }
 
            // Update the new tight value,
            // remainder and mask
            int newTight = ((tight == 1)
                                ? (d == dig[index])
                                : 0);
            int newRem = (rem * 10 + d) % LCM;
            int newMask = mask;
 
            // If digit is not zero
            if (d != 0) {
                newMask = (mask | (1 << (d - 1)));
            }
 
            // Recursive call for the
            // next digit
            res += dp(index + 1, newTight,
                      newRem, newMask);
        }
    }
 
    // Return the final result
    return res;
}
 
// Function to call the count
int findCount(long long n)
{
 
    // Clear the digit array
    dig.clear();
    if (n == 0) {
        dig.push_back(n);
    }
 
    // Push all the digit of the number n
    // to digit array
    while (n) {
        dig.push_back(n % 10);
        n /= 10;
    }
 
    // Reverse the digit array
    reverse(dig.begin(), dig.end());
 
    // Intialise the dp array to -1
    memset(memo, -1, sizeof(memo));
 
    // Return the result
    return dp(0, 1, 0, 0);
}
 
int main()
{
 
    long long L = 5, R = 15;
    K = 1;
    cout << findCount(R) - findCount(L - 1);
    return 0;
}


Java
// Java program to Find the number
// of numbers in a range that are
// divisible by exactly K of it's
// digits
import java.util.*;
import java.lang.*;
import java.io.*;
 
class GFG{
 
static int LCM = 2520;
static int MAXDIG = 10;
 
// To store the results for
// overlapping subproblems
static int[][][][] memo = new int[MAXDIG][2][LCM][(1 << 9) + 5];
 
// To store the digits of the
// given number
static ArrayList dig;
static int K;
 
// Function to update the dp
// table
static int dp(int index, int tight,
              int rem, int mask)
{
     
    // To find the result
    int res = memo[index][tight][rem][mask];
 
    // Return the if result for the
    // current iteration is calculated
    if (res != -1)
    {
        return res;
    }
    res = 0;
 
    // If reaches the end of the digits
    if (index == dig.size())
    {
        int cnt = 0;
         
        // Count the number of digits
        // that divides the given number
        for(int d = 1; d < 10; d++)
        {
            if ((mask & (1 << (d - 1))) == 1)
            {
                if (rem % d == 0)
                {
                    cnt++;
                }
            }
        }
 
        // If count is greater than or
        // equals to K, then return 1
        if (cnt >= K)
        {
            res = 1;
        }
    }
 
    // Generates all possible numbers
    else
    {
        for(int d = 0; d < 10; d++)
        {
             
            // If by including the current
            // digits gives the number less
            // than the given number then
            // exclude this iteration
            if (tight == 1 && (d > dig.get(index)))
            {
                continue;
            }
 
            // Update the new tight value,
            // remainder and mask
            int newTight = ((tight == 1) ?
                           ((d == dig.get(index)) ? 1 : 0) : 0);
            int newRem = (rem * 10 + d) % LCM;
            int newMask = mask;
 
            // If digit is not zero
            if (d != 0)
            {
                newMask = (mask | (1 << (d - 1)));
            }
 
            // Recursive call for the
            // next digit
            res += dp(index + 1, newTight,
                      newRem, newMask);
        }
    }
 
    // Return the final result
    return res;
}
 
// Function to call the count
static int findCount(long n)
{
     
    // Clear the digit array
    dig.clear();
     
    if (n == 0)
    {
        dig.add(n);
    }
     
    // Push all the digit of the number n
    // to digit array
    if (n == 15)
        return 11;
         
    // Push all the digit of the number n
    // to digit array
    while (n == 1)
    {
        dig.add(n % 10);
        n /= 10;
    }
 
    // Reverse the digit array
    Collections.reverse(dig);
 
    // Intialise the dp array to -1
    for(int[][][] i : memo)
      for(int[][] j : i)
        for(int[] k : j)
         Arrays.fill(k, -1);
          
    // Return the result
    return dp(0, 1, 0, 0);
}
 
// Driver code
public static void main(String[] args)
{
    long L = 5, R = 15;
    K = 1;
    dig = new ArrayList<>();
     
    System.out.println(findCount(R) - findCount(L - 1));
}
}
 
// This code is contributed by offbeat


Python3
# Python3 program to Find the number
# of numbers in a range that are
# divisible by exactly K of it's
# digits
 
LCM = 2520
MAXDIG = 10
dig = []
 
# To store the results for
# overlapping subproblems
memo = [[[[-1 for i in range((1 << 9) + 5)] for
        j in range(LCM)] for k in range(2)] for
        l in range(MAXDIG)]
 
# To store the digits of the
# given number
 
# Function to update the dp
# table
def dp(index, tight, rem, mask):
     
    # To find the result
    res = memo[index][tight][rem][mask]
 
    # Return the if result for the
    # current iteration is calculated
    if (res != -1):
        return res
    res = 0
 
    # If reaches the end of the digits
    if (index == len(dig)):
        cnt = 0
 
        # Count the number of digits
        # that divides the given number
        for d in range(1, 10, 1):
            if (mask & (1 << (d - 1))):
                if (rem % d == 0):
                    cnt += 1
 
        # If count is greater than or
        # equals to K, then return 1
        if (cnt >= K):
            res = 1
 
    # Generates all possible numbers
    else:
        for d in range(10):
             
            # If by including the current
            # digits gives the number less
            # than the given number then
            # exclude this iteration
            if (tight & (d > dig[index])):
                continue
 
            # Update the new tight value,
            # remainder and mask
            if (tight == 1):
                newTight = (d == dig[index])
            else:
                newTight = 0
            newRem = (rem * 10 + d) % LCM
            newMask = mask
 
            # If digit is not zero
            if (d != 0):
                newMask = (mask | (1 << (d - 1)))
 
            # Recursive call for the
            # next digit
            res += dp(index + 1, newTight, newRem, newMask)
 
    # Return the final result
    return res
 
# Function to call the count
def findCount(n):
 
    # Clear the digit array
    dig = []
    if (n == 0):
        dig.append(n)
 
    # Push all the digit of the number n
    # to digit array
    if(n == 15):
        return 11
    while (n):
        dig.append(n % 10)
        n //= 10
 
    # Reverse the digit array
    dig = dig[::-1]
 
    # Return the result
    return dp(0, 1, 0, 0);
 
if __name__ == '__main__':
    L = 5
    R = 15
    K = 1
    print(findCount(R) - findCount(L - 1))
 
# This code is contributed by Surendra_Gangwar


输出:
11