📌  相关文章
📜  计算具有给定数字和的数字(小于或等于 N)

📅  最后修改于: 2021-09-17 16:09:19             🧑  作者: Mango

给定一个数字 N 和一个总和 S,找出 N 以内的数字总和等于 S 的数字的计数。

Examples:
Input : N = 100, S = 4
Output : 5
Upto 100 only 5 numbers(4, 13, 22, 31, 40)
can produce 4 as their sum of digits.

Input : N = 1000, S = 1
Output : 4
Upto 1000 only 4 numbers(1, 10, 100 and 1000) 
can produce 1 as their sum of digits.

我们可以做一个具有状态的数字DP(当前索引,当前构造的第i个数字的个数是否等于或小于由N的前i个数字组成的数字,当前构造的数字的数字总和)。

令 dp[i][tight][sum_so_far] 表示已考虑前 i 位数字的数,tight 表示当前构造的数字是否等于或小于由 N 的前 i 位数字组成的数字。如果tight 为真,则表示当前构造的数等于N的前i位构成的数。如果为false则表示当前构造的数小于N的前i位构成的数。 sum_so_far表示数字之和当前构建的数量。

基本情况:
如果 i = N 中的位数,sum_so_far = Sum,则 answer = 1 否则 answer 为 0。

过渡:
为了填充第 (i+1) 位数字,我们可以考虑以下 –
如果tight 为真,那么这意味着我们构造的数仍然等于由N 的前i 位数字组成的数。我们可以尝试从0 到N 的第(i+1) 位数字的当前可能的数字值。如果我们尝试数字多于第 (i+1) 位,那么构造的数字将变得大于 N 的前 i 位数字组成的数字,这将违反我们构造的数字应该 <= N 的性质。
如果tight 为false,则表示由前i-1 位数字构成的数字小于由N 的前i-1 位数字构成的数字,所以这意味着我们的数字永远不会超过N,因此我们可以选择任意一个从 0 到 9 的数字。
nsum_so_far 可以通过将 sum_so_far 和当前数字(currdigit)相加得到。

最后,我们将返回答案,它是 N 以内的数字总和等于 S 的数量。

C++
#include 
using namespace std;
  
// N can be max 10^18 and hence digitsum will be 162 maximum.
long long dp[18][2][162];
  
long long solve(int i, bool tight, int sum_so_far,
                int Sum, string number, int len)
{
    if (i == len) {
  
        // If sum_so_far equals to given sum then 
        // return 1 else 0
        if (sum_so_far == Sum)
            return 1;
        else
            return 0;
    }
  
    long long& ans = dp[i][tight][sum_so_far];
    if (ans != -1) {
        return ans;
    }
  
    ans = 0;
    bool ntight;
    int nsum_so_far;
    for (char currdigit = '0'; currdigit <= '9'; currdigit++) {
  
        // Our constructed number should not become
        // greater than N.
        if (!tight && currdigit > number[i]) {
            break;
        }
  
        // If tight is true then it will also be true for (i+1) digit.
        ntight = tight || currdigit < number[i];
        nsum_so_far = sum_so_far + (currdigit - '0');
        ans += solve(i + 1, ntight, nsum_so_far, Sum, number, len);
    }
  
    return ans;
}
  
// Driver code
int main()
{
    long long count = 0;
    long long sum = 4;
    string number = "100";
    memset(dp, -1, sizeof dp);
    cout << solve(0, 0, 0, sum, number, number.size());
    return 0;
}


Java
// Java program to count 2s from 0 to n 
class GFG 
{
  
// N can be max 10^18 and hence 
// digitsum will be 162 maximum. 
static long dp[][][] = new long[18][2][162]; 
  
static long solve(int i, boolean tight, int sum_so_far, 
                int Sum, String number, int len) 
{ 
    if (i == len) 
    { 
  
        // If sum_so_far equals to given sum then 
        // return 1 else 0 
        if (sum_so_far == Sum) 
            return 1; 
        else
            return 0; 
    } 
  
    long ans = dp[i][1][sum_so_far]; 
    if (ans != -1) 
    { 
        return ans; 
    } 
  
    ans = 0; 
    boolean ntight; 
    int nsum_so_far; 
    for (char currdigit = '0'; currdigit <= '9'; currdigit++) 
    { 
  
        // Our constructed number should not become 
        // greater than N. 
        if (!tight && currdigit > number.charAt(i))
        { 
            break; 
        } 
  
        // If tight is true then it will 
        // also be true for (i+1) digit. 
        ntight = tight || currdigit < number.charAt(i); 
        nsum_so_far = sum_so_far + (currdigit - '0'); 
        ans += solve(i + 1, ntight, nsum_so_far, 
                        Sum, number, len); 
    } 
    return ans; 
} 
  
// Driver code 
public static void main(String[] args) 
{
    long count = 0; 
    int sum = 4; 
    String number = "100"; 
    for(int i = 0; i < 18; i++)
    {
        for(int j = 0; j < 2; j++)
        {
            for(int k = 0; k < 162; k++)
            dp[i][j][k] = -1;
        }
    }
    System.out.println( solve(0, false, 0, sum, 
                        number, number.length())); 
    }
} 
  
// This code is contributed by PrinciRaj1992


Python3
# Python3 implementation of the given approach. 
  
def solve(i, tight, sum_so_far, Sum, number, length): 
  
    if i == length: 
  
        # If sum_so_far equals to given 
        # sum then return 1 else 0 
        if sum_so_far == Sum: 
            return 1
        else:
            return 0
      
    ans = dp[i][tight][sum_so_far] 
    if ans != -1: 
        return ans 
      
    ans = 0
    for currdigit in range(0, 10): 
  
        currdigitstr = str(currdigit)
          
        # Our constructed number should 
        # not become greater than N. 
        if not tight and currdigitstr > number[i]: 
            break
  
        # If tight is true then it will also be true for (i+1) digit. 
        ntight = tight or currdigitstr < number[i] 
        nsum_so_far = sum_so_far + currdigit 
        ans += solve(i + 1, ntight, nsum_so_far, Sum, number, length) 
      
    return ans 
  
# Driver code 
if __name__ == "__main__":
  
    count, Sum = 0, 4
    number = "100"
    dp = [[[-1 for i in range(162)] for j in range(2)] for k in range(18)]
    print(solve(0, 0, 0, Sum, number, len(number)))
  
# This code is contributed by Rituraj Jain


C#
// C# program to count 2s from 0 to n 
using System;
  
class GFG 
{ 
  
    // N can be max 10^18 and hence 
    // digitsum will be 162 maximum. 
    static long [ , , ]dp = new long[18,2,162]; 
  
    static long solve(int i, bool tight, int sum_so_far, 
                    int Sum, String number, int len) 
    { 
        if (i == len) 
        { 
  
            // If sum_so_far equals to given sum then 
            // return 1 else 0 
            if (sum_so_far == Sum) 
                return 1; 
            else
                return 0; 
        } 
  
        long ans = dp[i,1,sum_so_far]; 
        if (ans != -1) 
        { 
            return ans; 
        } 
  
        ans = 0; 
        bool ntight; 
        int nsum_so_far; 
        for (char currdigit = '0'; currdigit <= '9'; currdigit++) 
        { 
  
            // Our constructed number should not become 
            // greater than N. 
            if (!tight && currdigit > number[i]) 
            { 
                break; 
            } 
  
            // If tight is true then it will 
            // also be true for (i+1) digit. 
            ntight = tight || currdigit < number[i]; 
            nsum_so_far = sum_so_far + (currdigit - '0'); 
            ans += solve(i + 1, ntight, nsum_so_far, 
                            Sum, number, len); 
        } 
        return ans; 
    } 
  
    // Driver code 
    public static void Main(String[] args) 
    { 
        int sum = 4; 
        String number = "100"; 
        for(int i = 0; i < 18; i++) 
        { 
            for(int j = 0; j < 2; j++) 
            { 
                for(int k = 0; k < 162; k++) 
                dp[i,j,k] = -1; 
            } 
        } 
        Console.WriteLine( solve(0, false, 0, sum, 
                            number, number.Length)); 
        } 
} 
  
// This code has been contributed by Rajput-Ji


输出:
5

时间复杂度: 2(tight) * Sum * log 10(N) * 10(transitions) = 20*log 10(N)*Sum。

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程