📌  相关文章
📜  范围内数字不超过 K 个非零数字的计数

📅  最后修改于: 2021-09-17 16:03:05             🧑  作者: Mango

给定一个由两个正整数 L 和 R 以及一个正整数 K 表示的范围。找出该范围内数字不包含超过 K 个非零数字的计数。
例子:

Input : L = 1, R = 1000, K = 3
Output : 1000
Explanation : All the numbers from 1 to 1000 
are 3 digit numbers which obviously cannot 
contain more than 3 non zero digits.

Input : L = 9995, R = 10005
Output : 6
Explanation : Required numbers are 
10000, 10001, 10002, 10003, 10004 and 10005

先决条件:数字DP
可以有两种方法来解决此类问题,一种可以是组合解决方案,另一种可以是基于动态规划的解决方案。下面是使用数字动态规划方法解决此问题的详细方法。
动态规划解决方案:首先,如果我们能够计算到 R 所需的数字,即在 [0, R] 范围内,我们可以通过从 0 到 R 求解,轻松得出 [L, R] 范围内的答案,然后将求解后得到的答案从 0 减去 L – 1。现在,我们需要定义 DP 状态。
DP 状态

  • 既然我们可以考虑我们的号码作为数字序列,一个状态是,我们目前的位置,这个位置可以有值从0到18,如果我们处理的是数字高达10 18。在每次递归调用中,我们尝试通过放置 0 到 9 的数字来从左到右构建序列。
  • 第二个状态是计数,它定义了非零数字的数量,我们已经放置在我们试图构建的数字中。
  • 另一个状态是布尔变量tight ,它告诉我们要构建的数字已经小于R,因此在接下来的递归调用中,我们可以放置0到9之间的任何数字。如果数字没有变小,则最大限制我们可以放置的数字是 R 中当前位置的数字。

在最后的递归调用中,当我们到达最后一个位置时,如果非零数字的计数小于或等于 K,则返回 1,否则返回 0。
下面是上述方法的实现。

C++
// CPP Program to find the count of
// numbers in a range where the number
// does not contain more than K non
// zero digits
 
#include 
using namespace std;
 
const int M = 20;
 
// states - position, count, tight
int dp[M][M][2];
 
// K is the number of non zero digits
int K;
 
// This function returns the count of
// required numbers from 0 to num
int countInRangeUtil(int pos, int cnt, int tight,
                     vector num)
{
    // Last position
    if (pos == num.size()) {
        // If count of non zero digits
        // is less than or equal to K
        if (cnt <= K)
            return 1;
        return 0;
    }
 
    // If this result is already computed
    // simply return it
    if (dp[pos][cnt][tight] != -1)
        return dp[pos][cnt][tight];
 
    int ans = 0;
 
    // Maximum limit upto which we can place
    // digit. If tight is 1, means number has
    // already become smaller so we can place
    // any digit, otherwise num[pos]
    int limit = (tight ? 9 : num[pos]);
 
    for (int dig = 0; dig <= limit; dig++) {
        int currCnt = cnt;
 
        // If the current digit is nonzero
        // increment currCnt
        if (dig != 0)
            currCnt++;
 
        int currTight = tight;
 
        // At this position, number becomes
        // smaller
        if (dig < num[pos])
            currTight = 1;
 
        // Next recursive call
        ans += countInRangeUtil(pos + 1, currCnt,
                                currTight, num);
    }
    return dp[pos][cnt][tight] = ans;
}
 
// This function converts a number into its
// digit vector and uses above function to compute
// the answer
int countInRange(int x)
{
    vector num;
    while (x) {
        num.push_back(x % 10);
        x /= 10;
    }
    reverse(num.begin(), num.end());
 
    // Initialize dp
    memset(dp, -1, sizeof(dp));
    return countInRangeUtil(0, 0, 0, num);
}
 
// Driver Code to test above functions
int main()
{
    int L = 1, R = 1000;
    K = 3;
    cout << countInRange(R) - countInRange(L - 1) << endl;
 
    L = 9995, R = 10005, K = 2;
    cout << countInRange(R) - countInRange(L - 1) << endl;
    return 0;
}


Java
// Java Program to find the count of
// numbers in a range where the number
// does not contain more than K non
// zero digits
import java.util.*;
class Solution
{
static final int M = 20;
 
// states - position, count, tight
static int dp[][][]= new int[M][M][2];
 
// K is the number of non zero digits
static int K;
static Vector num;
 
// This function returns the count of
// required numbers from 0 to num
static int countInRangeUtil(int pos, int cnt, int tight )
{
    // Last position
    if (pos == num.size()) {
        // If count of non zero digits
        // is less than or equal to K
        if (cnt <= K)
            return 1;
        return 0;
    }
 
    // If this result is already computed
    // simply return it
    if (dp[pos][cnt][tight] != -1)
        return dp[pos][cnt][tight];
 
    int ans = 0;
 
    // Maximum limit upto which we can place
    // digit. If tight is 1, means number has
    // already become smaller so we can place
    // any digit, otherwise num[pos]
    int limit = (tight!=0 ? 9 : num.get(pos));
 
    for (int dig = 0; dig <= limit; dig++) {
        int currCnt = cnt;
 
        // If the current digit is nonzero
        // increment currCnt
        if (dig != 0)
            currCnt++;
 
        int currTight = tight;
 
        // At this position, number becomes
        // smaller
        if (dig < num.get(pos))
            currTight = 1;
 
        // Next recursive call
        ans += countInRangeUtil(pos + 1, currCnt, currTight);
    }
    return dp[pos][cnt][tight] = ans;
}
 
// This function converts a number into its
// digit vector and uses above function to compute
// the answer
static int countInRange(int x)
{
    num= new Vector();
    while (x!=0) {
        num.add(x % 10);
        x /= 10;
    }
    Collections.reverse(num);
 
    // Initialize dp
    for(int i=0;i


Python3
# Python Program to find the count of
# numbers in a range where the number
# does not contain more than K non
# zero digits
 
# This function returns the count of
# required numbers from 0 to num
def countInRangeUtil(pos, cnt, tight, num):
 
    # Last position
    if pos == len(num):
 
        # If count of non zero digits
        # is less than or equal to K
        if cnt <= K:
            return 1
        return 0
 
    # If this result is already computed
    # simply return it
    if dp[pos][cnt][tight] != -1:
        return dp[pos][cnt][tight]
 
    ans = 0
 
    # Maximum limit upto which we can place
    # digit. If tight is 1, means number has
    # already become smaller so we can place
    # any digit, otherwise num[pos]
    limit = 9 if tight else num[pos]
 
    for dig in range(limit + 1):
        currCnt = cnt
 
        # If the current digit is nonzero
        # increment currCnt
        if dig != 0:
            currCnt += 1
 
        currTight = tight
 
        # At this position, number becomes
        # smaller
        if dig < num[pos]:
            currTight = 1
 
        # Next recursive call
        ans += countInRangeUtil(pos + 1, currCnt, currTight, num)
 
    dp[pos][cnt][tight] = ans
    return dp[pos][cnt][tight]
 
# This function converts a number into its
# digit vector and uses above function to compute
# the answer
def countInRange(x):
    global dp, K, M
 
    num = []
    while x:
        num.append(x % 10)
        x //= 10
 
    num.reverse()
 
    # Initialize dp
    dp = [[[-1, -1] for i in range(M)] for j in range(M)]
    return countInRangeUtil(0, 0, 0, num)
 
# Driver Code
if __name__ == "__main__":
 
    # states - position, count, tight
    dp = []
    M = 20
 
    # K is the number of non zero digits
    K = 0
 
    L = 1
    R = 1000
    K = 3
    print(countInRange(R) - countInRange(L - 1))
 
    L = 9995
    R = 10005
    K = 2
    print(countInRange(R) - countInRange(L - 1))
 
# This code is contributed by
# sanjeev2552


C#
// C# Program to find the count of
// numbers in a range where the number
// does not contain more than K non
// zero digits
using System;
using System.Collections.Generic;
 
class GFG
{
     
static int M = 20;
 
// states - position, count, tight
static int [,,]dp = new int[M, M, 2];
 
// K is the number of non zero digits
static int K;
static List num;
 
// This function returns the count of
// required numbers from 0 to num
static int countInRangeUtil(int pos,
                int cnt, int tight )
{
    // Last position
    if (pos == num.Count)
    {
        // If count of non zero digits
        // is less than or equal to K
        if (cnt <= K)
            return 1;
        return 0;
    }
 
    // If this result is already computed
    // simply return it
    if (dp[pos, cnt, tight] != -1)
        return dp[pos, cnt, tight];
 
    int ans = 0;
 
    // Maximum limit upto which we can place
    // digit. If tight is 1, means number has
    // already become smaller so we can place
    // any digit, otherwise num[pos]
    int limit = (tight != 0 ? 9 : num[pos]);
 
    for (int dig = 0; dig <= limit; dig++)
    {
        int currCnt = cnt;
 
        // If the current digit is nonzero
        // increment currCnt
        if (dig != 0)
            currCnt++;
 
        int currTight = tight;
 
        // At this position, number becomes
        // smaller
        if (dig < num[pos])
            currTight = 1;
 
        // Next recursive call
        ans += countInRangeUtil(pos + 1, currCnt, currTight);
    }
    return dp[pos,cnt,tight] = ans;
}
 
// This function converts a number into its
// digit vector and uses above function to compute
// the answer
static int countInRange(int x)
{
    num = new List();
    while (x != 0)
    {
        num.Add(x % 10);
        x /= 10;
    }
    num.Reverse();
 
    // Initialize dp
    for(int i = 0; i < M; i++)
        for(int j = 0; j < M; j++)
            for(int k = 0; k < 2; k++)
            dp[i, j, k] = -1;
    return countInRangeUtil(0, 0, 0);
}
 
// Driver Code
public static void Main()
{
    int L = 1, R = 1000;
    K = 3;
    Console.WriteLine( countInRange(R) - countInRange(L - 1) );
 
    L = 9995; R = 10005; K = 2;
    Console.WriteLine( countInRange(R) - countInRange(L - 1) );
}
}
 
/* This code contributed by PrinciRaj1992 */


Javascript


输出:
1000
6

时间复杂度: O(18 * 18 * 2 * 10),如果我们处理的数字高达 10 18