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

📅  最后修改于: 2021-09-17 07:45:26             🧑  作者: Mango

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

例子:

朴素的方法:对于小的N值,循环遍历范围[0, N]并检查数字的数字总和是否是K 的倍数。

高效方法:思路是用digit dp来解决这个问题。将解决从给定整数的左边或最高有效位 (MSD) 开始的所有索引值迭代的子问题,并且对于每个索引,存储方式的数量,使得(直到当前索引的数字总和)mod K为零。 dp 状态将是:

假设我们在具有索引idx的 MSD。所以最初总和将为0 。在每个位置,设置一个始终在[0, 9]范围内的限制。
因此,用从0到 limit 范围内的数字填充 index 处的数字,并从具有index = 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


Javascript


输出:
635270835

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程