给定一个数字N和一个和S,找到数字总和等于S的直到N的数字的计数。
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.
我们可以做一个具有状态(当前索引,当前构造的i位数是否等于或小于N的前i个数字构成的数字,即当前构造的位数的总和)的数字DP。
令dp [i] [tight] [sum_so_far]表示已考虑其前i个数字的数字的计数,tight表示当前构造的数字是否等于或小于N的前i个数字形成的数字。则表示当前构造的数字等于N的前i个数字构成的数字。如果为false,则表示当前构造的数字小于N的前i个数字构成的数字。sum_so_far表示数字的总和。当前构造的数量。
基本情况:
如果i = N中的位数,sum_so_far = Sum,则答案= 1,否则答案为0。
过渡:
为了填写第(i + 1)位数字,我们可以考虑以下内容–
如果tight为true,则表示我们的构造数仍等于由N的第i个数字形成的数字。我们可以尝试从0到N的第i + 1个数字的当前可能的数字值。如果数字比第(i + 1)位数字大,则构造的数字将大于N的前i个数字形成的数字,这将违反我们的构造数字应<= N的性质。
如果tight为假,则表示从第i个数字至第i个数字构造的数字已小于从第i个数字N由第i个数字构造的数字,因此这意味着我们的数字永远不能超过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(紧密)*总和* log 10(N)* 10(转换)= 20 * log 10(N)*总和。