📌  相关文章
📜  任意两个相邻数字之间绝对差最多为 K 的数字的计数

📅  最后修改于: 2021-09-03 03:48:55             🧑  作者: Mango

给定一个整数 N ,任务是计算直到N的数字,在任何两个相邻数字之间具有至多K的绝对差。
注意:对于任何数字,整数 0 的计数是相当可观的。
例子:

朴素的方法:这个想法是迭代到 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


输出:
15

时间复杂度: O( D * 10 * 2 * 2 * 10),考虑到 N 有D 个数字。

如果您想与行业专家一起参加直播课程,请参阅Geeks Classes Live