📌  相关文章
📜  [0,N]范围内的整数计数,其数字总和是K的倍数

📅  最后修改于: 2021-04-30 02:11:40             🧑  作者: Mango

给定两个整数NK ,任务是计算数字和为K的倍数的[0,N]范围内的整数数。答案可能很大,因此以10 9 +7为模数打印答案。

例子:

天真的方法:对于较小的N ,请在[0,N]范围内循环并检查数字的总和是否为K的倍数。

高效的方法:想法是使用digit dp解决此问题。将解决给定整数中从左或最高有效位(MSD)遍历所有索引值的子问题,并且对于每个索引,存储使( K到当前索引的位数的总和)mod K为零的方式数。 dp状态将为:

假设我们在MSD上具有索引idx 。因此,最初的总和为0 。在每个位置处设置一个始终在[0,9]范围内的极限。
因此,用从0到极限的数字填充索引处的数字,并从索引= idx + 1的下一个状态获取答案,并分别计算下一个状态的new_tight 。 dp状态定义为:

下面是上述方法的实现:

C++
// C++ implementation of the approach
#include 
using namespace std;
  
#define MAX 100005
#define MOD 1000000007
  
// To store the states of the dp
int dp[MAX][101][2];
  
// Function to return the count of numbers
// from the range [0, n] whose digit sum
// is a multiple of k using bottom-up dp
int countNum(int idx, int sum, int tight,
             vector num, int len, int k)
{
    if (len == idx) {
        if (sum == 0)
            return 1;
        else
            return 0;
    }
    if (dp[idx][sum][tight] != -1)
        return dp[idx][sum][tight];
    int res = 0, limit;
  
    // The digit in this index can
    // only be from [0, num[idx]]
    if (tight == 0) {
        limit = num[idx];
    }
  
    // The digit in this index can
    // be anything from [0, 9]
    else {
        limit = 9;
    }
    for (int i = 0; i <= limit; i++) {
  
        // new_tight is the flag value
        // for the next position
        int new_tight = tight;
        if (tight == 0 && i < limit)
            new_tight = 1;
        res += countNum(idx + 1,
                        (sum + i) % k, new_tight,
                        num, len, k);
        res %= MOD;
    }
  
    // res can't be negative
    if (res < 0)
        res += MOD;
    return dp[idx][sum][tight] = res;
}
  
// Function to process the string to
// a vector of digits from MSD to LSD
vector process(string s)
{
    vector num;
    for (int i = 0; i < s.length(); i++) {
        num.push_back(s[i] - '0');
    }
    return num;
}
  
// Driver code
int main()
{
  
    // For large input number n
    string n = "98765432109876543210";
  
    // Total number of digits in n
    int len = n.length();
  
    int k = 58;
  
    // Clean dp table
    memset(dp, -1, sizeof(dp));
  
    // Process the string to a vector
    // of digits from MSD to LSD
    vector num = process(n);
  
    cout << countNum(0, 0, 0, num, len, k);
  
    return 0;
}


Java
// Java implementation of the approach
import java.util.*;
  
class GFG
{
  
static final int MAX = 100005;
static final int MOD = 1000000007;
  
// To store the states of the dp
static int [][][]dp = new int[MAX][101][2];
  
// Function to return the count of numbers
// from the range [0, n] whose digit sum
// is a multiple of k using bottom-up dp
static int countNum(int idx, int sum, int tight,
            Vector num, int len, int k)
{
    if (len == idx) 
    {
        if (sum == 0)
            return 1;
        else
            return 0;
    }
    if (dp[idx][sum][tight] != -1)
        return dp[idx][sum][tight];
    int res = 0, limit;
  
    // The digit in this index can
    // only be from [0, num[idx]]
    if (tight == 0) 
    {
        limit = num.get(idx);
    }
  
    // The digit in this index can
    // be anything from [0, 9]
    else
    {
        limit = 9;
    }
    for (int i = 0; i <= limit; i++)
    {
  
        // new_tight is the flag value
        // for the next position
        int new_tight = tight;
        if (tight == 0 && i < limit)
            new_tight = 1;
        res += countNum(idx + 1,
                        (sum + i) % k, new_tight,
                        num, len, k);
        res %= MOD;
    }
  
    // res can't be negative
    if (res < 0)
        res += MOD;
    return dp[idx][sum][tight] = res;
}
  
// Function to process the String to
// a vector of digits from MSD to LSD
static Vector process(String s)
{
    Vector num = new Vector();
    for (int i = 0; i < s.length(); i++)
    {
        num.add(s.charAt(i) - '0');
    }
    return num;
}
  
// Driver code
public static void main(String[] args)
{
  
    // For large input number n
    String n = "98765432109876543210";
  
    // Total number of digits in n
    int len = n.length();
  
    int k = 58;
  
    // Clean dp table
    for(int i = 0; i < MAX; i++)
    {
        for(int j = 0; j < 101; j++)
        {
            for(int l = 0; l < 2; l++)
            dp[i][j][l] = -1;
        }
    }
  
    // Process the String to a vector
    // of digits from MSD to LSD
    Vector num = process(n);
  
    System.out.print(countNum(0, 0, 0, num, len, k));
  
}
}
  
// This code is contributed by 29AjayKumar


Python 3
# Python 3 implementation of the approach
MAX = 10005
MOD = 1000000007
  
# Function to return the count of numbers
# from the range [0, n] whose digit sum
# is a multiple of k using bottom-up dp
def countNum(idx, sum, tight, num, len1, k):
    if (len1 == idx):
        if (sum == 0):
            return 1
        else:
            return 0
    if (dp[idx][sum][tight] != -1):
        return dp[idx][sum][tight]
    res = 0
  
    # The digit in this index can
    # only be from [0, num[idx]]
    if (tight == 0):
        limit = num[idx]
  
    # The digit in this index can
    # be anything from [0, 9]
    else:
        limit = 9
    for i in range(limit + 1):
          
        # new_tight is the flag value
        # for the next position
        new_tight = tight
        if (tight == 0 and i < limit):
            new_tight = 1
        res += countNum(idx + 1,(sum + i) % k, 
                      new_tight, num, len1, k)
        res %= MOD
  
    # res can't be negative
    if (res < 0):
        res += MOD
    dp[idx][sum][tight] = res
    return dp[idx][sum][tight]
  
# Function to process the string to
# a vector of digits from MSD to LSD
def process(s):
    num = []
    for i in range(len(s)):
        num.append(ord(s[i]) - ord('0'))
    return num
  
# Driver code
if __name__ == '__main__':
      
    # For large input number n
    n = "98765432109876543210"
  
    # Total number of digits in n
    len1 = len(n)
  
    k = 58
      
    # To store the states of the dp
    dp = [[[-1 for i in range(2)]
               for j in range(101)] 
               for k in range(MAX)]
  
    # Process the string to a vector
    # of digits from MSD to LSD
    num = process(n)
  
    print(countNum(0, 0, 0, num, len1, k))
  
# This code is contributed by Surendra_Gangwar


C#
// C# implementation of the approach
using System;
using System.Collections.Generic;
  
class GFG
{
static readonly int MAX = 10005;
static readonly int MOD = 1000000007;
  
// To store the states of the dp
static int [,,]dp = new int[MAX, 101, 2];
  
// Function to return the count of numbers
// from the range [0, n] whose digit sum
// is a multiple of k using bottom-up dp
static int countNum(int idx, int sum, int tight,
              List num, int len, int k)
{
    if (len == idx) 
    {
        if (sum == 0)
            return 1;
        else
            return 0;
    }
      
    if (dp[idx, sum, tight] != -1)
        return dp[idx, sum, tight];
    int res = 0, limit;
  
    // The digit in this index can
    // only be from [0, num[idx]]
    if (tight == 0) 
    {
        limit = num[idx];
    }
  
    // The digit in this index can
    // be anything from [0, 9]
    else
    {
        limit = 9;
    }
    for (int i = 0; i <= limit; i++)
    {
  
        // new_tight is the flag value
        // for the next position
        int new_tight = tight;
        if (tight == 0 && i < limit)
            new_tight = 1;
        res += countNum(idx + 1,
                       (sum + i) % k, new_tight,
                        num, len, k);
        res %= MOD;
    }
  
    // res can't be negative
    if (res < 0)
        res += MOD;
    return dp[idx, sum, tight] = res;
}
  
// Function to process the String to
// a vector of digits from MSD to LSD
static List process(String s)
{
    List num = new List();
    for (int i = 0; i < s.Length; i++)
    {
        num.Add(s[i] - '0');
    }
    return num;
}
  
// Driver code
public static void Main(String[] args)
{
  
    // For large input number n
    String n = "98765432109876543210";
  
    // Total number of digits in n
    int len = n.Length;
  
    int k = 58;
  
    // Clean dp table
    for(int i = 0; i < MAX; i++)
    {
        for(int j = 0; j < 101; j++)
        {
            for(int l = 0; l < 2; l++)
            dp[i, j, l] = -1;
        }
    }
  
    // Process the String to a vector
    // of digits from MSD to LSD
    List num = process(n);
  
    Console.Write(countNum(0, 0, 0, num, len, k));
}
}
  
// This code is contributed by Rajput-Ji


输出:
635270835