给定一个数字 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 现场工作专业课程和学生竞争性编程现场课程。