给定一个整数 N ,任务是计算直到N的数字,在任何两个相邻数字之间具有至多K的绝对差。
注意:对于任何数字,整数 0 的计数是相当可观的。
例子:
Input: N = 20, K = 2
Output: 15
Explanation:
The required numbers are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 and 20. Notice that 14, 15, 16, 17, 18 and 19 all have adjacent digit’s absolute difference greater than K = 2 and thus they are not counted.
Input: N = 30, K = 3
Output: 22
Explanation:
All numbers upto 30 except 15, 16, 17, 18, 19, 26, 27, 28, 29 are accepted.
朴素的方法:这个想法是迭代到 N 并检查是否存在 K 的差异的所有数字。如果是,则计算它,否则跳过该数字并继续迭代。
时间复杂度: O(N)
辅助空间: O(1)
有效的方法:这个问题可以使用数字动态规划来优化。以下是给定问题的详细 dp 状态。
- 在 Digit Dp 中,我们将数字视为一个数字序列,因此需要一个状态位置,以便标记我们当前所处的状态。在每次递归调用中,尝试通过放置 0 到 9 之间的数字并增加位置来从左到右构建序列。
- 前一个数字只存储那些与前一个数字的绝对差最多为K的数字。因此,前一位数字需要另一个状态。
- Tight ,state 告诉我们要构建的数字是否已经小于 N,以便在接下来的递归调用中我们可以放置 0 到 9 之间的任何数字。否则,我们可以放置直到当前位置的N的数字.
- 初始化一个布尔变量Start ,它告诉数字是否已经开始。如果号码还没有开始,我们可以通过在当前位置相对于紧的上限放置从1到上限的数字来开始号码。否则,在不开始数字的情况下向前重复。
- 在每次递归调用中,相对于前一个数字设置当前数字,以便它们之间的绝对差异永远不会超过K 。在基本情况下,如果到达最后一个位置,则返回 1。
下面是上述方法的实现:
C++
// C++ program to get the count
// of numbers upto N having
// absolute difference at most K
// between any two adjacent digits
#include
using namespace std;
// Table to store solution
// of each subproblem
long long dp[1002][10][2][2];
// Function to calculate
// all possible numbers
long long possibleNumbers(
int pos, int previous, bool tight,
bool start, string N, int K)
{
// Check if position reaches end that is
// is equal to length of N
if (pos == N.length())
return 1;
// Check if the result is
// already computed
// simply return it
if (dp[pos][previous][tight][start] != -1)
return dp[pos][previous][tight][start];
int res = 0;
// Maximum limit upto which we can place
// digit. If tight is false, means number has
// already become smaller so we can place
// any digit, otherwise N[pos]
int upper_limit
= (tight)
? (N[pos] - '0')
: 9;
int new_tight;
// Chekc if start is false the number
// has not started yet
if (!start) {
// Check if we do not start
// the number at pos
// then recur forward
res = possibleNumbers(
pos + 1, previous,
false, false, N, K);
// If we start the number
// we can place any digit
// from 1 to upper_limit
for (int i = 1; i <= upper_limit; i++) {
// Finding the new tight
new_tight
= (tight
&& i == upper_limit)
? 1
: 0;
res += possibleNumbers(
pos + 1, i, new_tight,
true, N, K);
}
}
// Condition if the number
// has already started
else {
// We can place digit upto
// upperbound & absolute difference
// with previous digit much
// be atmost K
for (int i = 0; i <= upper_limit; i++) {
new_tight = (tight
&& i == upper_limit)
? 1
: 0;
// Absolute difference atmost K
if (abs(i - previous) <= K)
res += possibleNumbers(
pos + 1, i,
new_tight, true, N, K);
}
}
// Store the solution
// to this subproblem
dp[pos][previous][tight][start] = res;
return dp[pos][previous][tight][start];
}
// Driver code
int main(void)
{
string N = "20";
int K = 2;
// Initialising the
// table with -1
memset(dp, -1, sizeof dp);
// Function call
cout << possibleNumbers(
0, 0, true,
false, N, K)
<< endl;
}
Java
// Java program to get the count
// of numbers upto N having
// absolute difference at most K
// between any two adjacent digits
import java.util.*;
class GFG{
// Table to store solution
// of each subproblem
static int [][][][]dp = new int[1002][10][2][2];
// Function to calculate
// all possible numbers
static int possibleNumbers(int pos, int previous,
int tight, int start,
String N, int K)
{
// Check if position reaches end
// that is equal to length of N
if (pos == N.length())
return 1;
// Check if the result is already
// computed simply return it
if (dp[pos][previous][tight][start] != -1)
return dp[pos][previous][tight][start];
int res = 0;
// Maximum limit upto which we can
// place digit. If tight is false,
// means number has already become
// smaller so we can place
// any digit, otherwise N[pos]
int upper_limit = (tight == 1) ?
(N.charAt(pos) - '0') : 9;
int new_tight;
// Check if start is false the number
// has not started yet
if (start == 0)
{
// Check if we do not start
// the number at pos
// then recur forward
res = possibleNumbers(pos + 1, previous,
0, 0, N, K);
// If we start the number
// we can place any digit
// from 1 to upper_limit
for(int i = 1; i <= upper_limit; i++)
{
// Finding the new tight
new_tight = (tight > 0 &&
i == upper_limit) ? 1 : 0;
res += possibleNumbers(pos + 1, i,
new_tight,
1, N, K);
}
}
// Condition if the number
// has already started
else
{
// We can place digit upto
// upperbound & absolute difference
// with previous digit much
// be atmost K
for(int i = 0; i <= upper_limit; i++)
{
new_tight = (tight > 0 &&
i == upper_limit) ? 1 : 0;
// Absolute difference atmost K
if (Math.abs(i - previous) <= K)
res += possibleNumbers(pos + 1, i,
new_tight, 1,
N, K);
}
}
// Store the solution
// to this subproblem
dp[pos][previous][tight][start] = res;
return dp[pos][previous][tight][start];
}
// Driver code
public static void main(String[] args)
{
String N = "20";
int K = 2;
// Initialising the
// table with -1
for(int i = 0; i < 1002; i++)
for(int j = 0; j < 10; j++)
for(int k = 0; k < 2; k++)
for(int l = 0; l < 2; l++)
dp[i][j][k][l] = -1;
// Function call
System.out.print(possibleNumbers(0, 0, 1, 0,
N, K) + "\n");
}
}
// This code is contributed by Princi Singh
Python3
# Python3 program to get the count of
# numbers upto N having absolute
# difference at most K between any
# two adjacent digits
# Table to store solution
# of each subproblem
dp = [[[[ -1 for i in range(2)]
for j in range(2)]
for i in range(10)]
for j in range(1002)]
# Function to calculate
# all possible numbers
def possibleNumber(pos, previous, tight,
start, N, K):
# Check if position reaches end
# that is equal to length of N
if(pos == len(N)):
return 1
# Check if the result is
# already computed
# simply return it
if(dp[pos][previous][tight][start] != -1):
return dp[pos][previous][tight][start]
res = 0
# Maximum limit upto which we can place
# digit. If tight is false, means number has
# already become smaller so we can place
# any digit, otherwise N[pos]
if(tight):
upper_limit = ord(N[pos]) - ord('0')
else:
upper_limit = 9
# Chekc if start is false the number
# has not started yet
if(not start):
# Check if we do not start
# the number at pos
# then recur forward
res = possibleNumber(pos + 1, previous,
False, False, N, K)
# If we start the number
# we can place any digit
# from 1 to upper_limit
for i in range(1, upper_limit + 1):
# Finding the new tight
if(tight and i == upper_limit):
new_tight = 1
else:
new_tight = 0
res += possibleNumber(pos + 1, i,
new_tight,
True, N, K)
# Condition if the number
# has already started
else:
# We can place digit upto
# upperbound & absolute
# difference with previous
# digit much be atmost K
for i in range(upper_limit + 1):
if(tight and i == upper_limit):
new_tight = 1
else:
new_tight = 0
# Absolute difference atmost K
if(abs(i - previous) <= K):
res += possibleNumber(pos + 1, i,
new_tight,
True, N, K)
# Store the solution to this subproblem
dp[pos][previous][tight][start] = res
return dp[pos][previous][tight][start]
# Driver code
if __name__ == '__main__':
N = "20"
K = 2
# Function call
print(possibleNumber(0, 0, True,
False, N, K))
# This code is contributed by Shivam Singh
C#
// C# program to get the count
// of numbers upto N having
// absolute difference at most K
// between any two adjacent digits
using System;
class GFG{
// Table to store solution
// of each subproblem
static int [,,,]dp = new int[1002, 10, 2, 2];
// Function to calculate
// all possible numbers
static int possibleNumbers(int pos, int previous,
int tight, int start,
String N, int K)
{
// Check if position reaches end
// that is equal to length of N
if (pos == N.Length)
return 1;
// Check if the result is already
// computed simply return it
if (dp[pos, previous, tight, start] != -1)
return dp[pos, previous, tight, start];
int res = 0;
// Maximum limit upto which we can
// place digit. If tight is false,
// means number has already become
// smaller so we can place
// any digit, otherwise N[pos]
int upper_limit = (tight == 1) ?
(N[pos] - '0') : 9;
int new_tight;
// Check if start is false the number
// has not started yet
if (start == 0)
{
// Check if we do not start
// the number at pos
// then recur forward
res = possibleNumbers(pos + 1, previous,
0, 0, N, K);
// If we start the number
// we can place any digit
// from 1 to upper_limit
for(int i = 1; i <= upper_limit; i++)
{
// Finding the new tight
new_tight = (tight > 0 &&
i == upper_limit) ? 1 : 0;
res += possibleNumbers(pos + 1, i,
new_tight,
1, N, K);
}
}
// Condition if the number
// has already started
else
{
// We can place digit upto
// upperbound & absolute difference
// with previous digit much
// be atmost K
for(int i = 0; i <= upper_limit; i++)
{
new_tight = (tight > 0 &&
i == upper_limit) ? 1 : 0;
// Absolute difference atmost K
if (Math.Abs(i - previous) <= K)
res += possibleNumbers(pos + 1, i,
new_tight, 1,
N, K);
}
}
// Store the solution
// to this subproblem
dp[pos, previous, tight, start] = res;
return dp[pos, previous, tight, start];
}
// Driver code
public static void Main(String[] args)
{
String N = "20";
int K = 2;
// Initialising the
// table with -1
for(int i = 0; i < 1002; i++)
for(int j = 0; j < 10; j++)
for(int k = 0; k < 2; k++)
for(int l = 0; l < 2; l++)
dp[i, j, k, l] = -1;
// Function call
Console.Write(possibleNumbers(0, 0, 1, 0,
N, K) + "\n");
}
}
// This code is contributed by amal kumar choubey
Javascript
15
时间复杂度: O( D * 10 * 2 * 2 * 10),考虑到 N 有D 个数字。
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。