先决条件:动态编程,DigitDP
给定两个整数N和K。任务是找到1到N (含)之间的整数数,该整数数以10为基数时正好包含K个非零数字。
例子:
Input: N = 100, K = 1
Output: 19
Explanation:
The digits with exactly 1 non zero digits between 1 and 100 are:
1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 20, 30, 40, 50, 60, 70, 80, 90, 100
Input: N = 25, K = 2
Output: 14
Explanation:
The digits with exactly 2 non zero digits between 1 and 25 are:
11, 12, 13, 14, 15, 16, 17,
18, 19, 21, 22, 23, 24, 25
方法:考虑N位整数就足够了,如果需要,可以用0填充高位数字。通过应用称为数字DP的方法可以解决此问题。
- dp [i] [0] [j] =已确定较高的i个数字,并且有j个非零数字,并且已经确定它小于N。
- dp [i] [1] [j] =已确定较高的i个数字,并且有j个非零数字,并且尚未确定它小于N。
在计算完上述dp之后,所需的答案是dp [L] [0] [K] + dp [L] [1] [K] ,其中L是N的位数。
下面是上述方法的实现:
C++
// C++ implementation of the above approach
#include
using namespace std;
// Function to find number less than N
// having k non-zero digits
int k_nonzero_numbers(string s, int n, int k)
{
// Store the memorised values
int dp[n + 1][2][k + 1];
// Initialise
for (int i = 0; i <= n; i++)
for (int j = 0; j < 2; j++)
for (int x = 0; x <= k; x++)
dp[i][j][x] = 0;
// Base
dp[0][0][0] = 1;
// Calculate all states
// For every state, from numbers 1 to N,
// the count of numbers which contain exactly j
// non zero digits is being computed and updated
// in the dp array.
for (int i = 0; i < n; ++i) {
int sm = 0;
while (sm < 2) {
for (int j = 0; j < k + 1; ++j) {
int x = 0;
while (x <= (sm ? 9 : s[i] - '0')) {
dp[i + 1][sm || x < (s[i] - '0')][j + (x > 0)]
+= dp[i][sm][j];
++x;
}
}
++sm;
}
}
// Return the required answer
return dp[n][0][k] + dp[n][1][k];
}
// Driver code
int main()
{
string s = "25";
int k = 2;
int n = s.size();
// Function call
cout << k_nonzero_numbers(s, n, k);
return 0;
}
Java
// Java implementation of the above approach
class Geeks{
// Function to find number less than N
// having k non-zero digits
static int k_nonzero_numbers(String s, int n, int k)
{
// Store the memorised values
int dp[][][] = new int[n + 1][2][k + 1];
// Initialise
for(int i = 0; i <= n; i++)
for(int j = 0; j < 2; j++)
for(int x = 0; x <= k; x++)
dp[i][j][x] = 0;
// Base
dp[0][0][0] = 1;
// Calculate all states
// For every state, from numbers 1 to N,
// the count of numbers which contain exactly j
// non zero digits is being computed and updated
// in the dp array.
for(int i = 0; i < n; ++i)
{
int sm = 0;
while (sm < 2)
{
for(int j = 0; j < k + 1; ++j)
{
int x = 0;
while (x <= (sm !=
0 ? 9 :s.charAt(i) - '0'))
{
if (j + (x > 0 ? 1 : 0) < k + 1)
{
dp[i + 1][(sm != 0 || x <
(s.charAt(i) - '0')) ?
1 : 0][j + (x > 0 ?
1 : 0)] += dp[i][sm][j];
}
++x;
}
}
++sm;
}
}
// Return the required answer
return dp[n][0][k] + dp[n][1][k];
}
// Driver code
public static void main(String[] args)
{
String s = "25";
int k = 2;
int n = s.length();
// Function call
System.out.println(k_nonzero_numbers(s, n, k));
}
}
// This code is contributed by Rajnis09
Python3
# Python3 implementation of the above approach
# Function to find number less than N
# having k non-zero digits
def k_nonzero_numbers(s, n, k):
# Store the memorised values
dp = [[[ 0 for i in range(k + 2)]
for i in range(2)]
for i in range(n + 2)]
# Initialise
for i in range(n + 1):
for j in range(2):
for x in range(k + 1):
dp[i][j][x] = 0
# Base
dp[0][0][0] = 1
# Calculate all states
# For every state, from numbers 1 to N,
# the count of numbers which contain
# exactly j non zero digits is being
# computed and updated in the dp array.
for i in range(n):
sm = 0
while (sm < 2):
for j in range(k + 1):
x = 0
y = 0
if sm:
y = 9
else:
y = ord(s[i]) - ord('0')
while (x <= y):
dp[i + 1][(sm or x < (
ord(s[i]) - ord('0')))][j +
(x > 0)] += dp[i][sm][j]
x += 1
sm += 1
# Return the required answer
return dp[n][0][k] + dp[n][1][k]
# Driver code
if __name__ == '__main__':
s = "25"
k = 2
n = len(s)
# Function call
print(k_nonzero_numbers(s, n, k))
# This code is contributed by mohit kumar 29
C#
// C# implementation of the above approach
using System;
using System.Collections;
class GFG{
// Function to find number less than N
// having k non-zero digits
static int k_nonzero_numbers(string s, int n,
int k)
{
// Store the memorised values
int [,,]dp = new int[n + 1, 2, k + 1];
// Initialise
for(int i = 0; i <= n; i++)
for(int j = 0; j < 2; j++)
for(int x = 0; x <= k; x++)
dp[i, j, x] = 0;
// Base
dp[0, 0, 0] = 1;
// Calculate all states
// For every state, from numbers 1 to N,
// the count of numbers which contain exactly j
// non zero digits is being computed and updated
// in the dp array.
for(int i = 0; i < n; ++i)
{
int sm = 0;
while (sm < 2)
{
for(int j = 0; j < k + 1; ++j)
{
int x = 0;
while (x <= (sm !=
0 ? 9 : s[i]- '0'))
{
if (j + (x > 0 ? 1 : 0) < k + 1)
{
dp[i + 1, ((sm != 0 || x <
(s[i] - '0')) ? 1 : 0),
j + (x > 0 ? 1 : 0)] +=
dp[i, sm, j];
}
++x;
}
}
++sm;
}
}
// Return the required answer
return dp[n, 0, k] + dp[n, 1, k];
}
// Driver code
public static void Main(string[] args)
{
string s = "25";
int k = 2;
int n = s.Length;
// Function call
Console.Write(k_nonzero_numbers(s, n, k));
}
}
// This code is contributed by rutvik_56
输出:
14
时间复杂度: O(LK),其中L是N中的位数。
注意:用于分别从[0,1]和[0,9]计算状态的两个for循环被视为常数乘法。