📌  相关文章
📜  在任何两个相邻数字之间的绝对差异最大为K的N个数的计数

📅  最后修改于: 2021-05-04 19:05:28             🧑  作者: 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位。