给定一个由两个正整数 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