给定一个范围[L, R]和两个正整数N和M 。任务是计算仅包含非零数字的范围内的数字,其数字总和等于 N并且该数字可被 M 整除。
例子:
Input: L = 1, R = 100, N = 8, M = 2
Output: 4
Only 8, 26, 44 and 62 are valid numbers
Input: L = 1, R = 200, N = 4, M = 11
Output: 2
Only 22 and 121 are valid numbers
先决条件:数字DP
方法:首先,如果我们能够计算到 R 所需的数字,即在 [0, R] 范围内,我们可以通过从 0 到 R 求解然后减去在 [L, R] 范围内轻松得出答案我们从 0 到 L-1 求解后得到的答案。现在,我们需要定义 DP 状态。
DP 状态:
- 既然我们可以考虑我们的号码作为数字序列,一个状态是,我们目前的位置,这个位置可以有值从0到18,如果我们用数字处理多达10 18。在每次递归调用中,我们尝试通过放置 0 到 9 的数字来从左到右构建序列。
- 第二个状态是我们到目前为止放置的数字的总和。
- 第三个状态是余数,它定义了我们迄今为止对 M 取模的数字的模数。
- 另一种状态是布尔变量tight ,它告诉我们要构建的数字已经变得小于R,因此在即将到来的递归调用中,我们可以放置0到9之间的任何数字。如果数字没有变小,则最大限制我们可以放置的数字是 R 中当前位置的数字。
对于只有非零数字的数字,我们维护一个变量nonz其值如果 1 告诉我们放置的数字中的第一个数字是非零数字,因此,现在我们不能在即将到来的数字中放置任何零数字调用。否则,我们可以放置一个零作为前导零,使当前数字的位数小于上限位数。
下面是上述方法的实现:
C++
// C++ implementation of the approach
#include
using namespace std;
const int M = 20;
// states - position, sum, rem, tight
// sum can have values upto 162, if we
// are dealing with numbers upto 10^18
// when all 18 digits are 9, then sum
// is 18 * 9 = 162
int dp[M][165][M][2];
// n is the sum of digits and number should
// be divisible by m
int n, m;
// Function to return the count of
// required numbers from 0 to num
int count(int pos, int sum, int rem, int tight,
int nonz, vector num)
{
// Last position
if (pos == num.size()) {
if (rem == 0 && sum == n)
return 1;
return 0;
}
// If this result is already computed
// simply return it
if (dp[pos][sum][rem][tight] != -1)
return dp[pos][sum][rem][tight];
int ans = 0;
// Maximum limit upto which we can place
// digit. If tight is 1, means number has
// already become smaller so we can place
// any digit, otherwise num[pos]
int limit = (tight ? 9 : num[pos]);
for (int d = 0; d <= limit; d++) {
// If the current digit is zero
// and nonz is 1, we can't place it
if (d == 0 && nonz)
continue;
int currSum = sum + d;
int currRem = (rem * 10 + d) % m;
int currF = tight || (d < num[pos]);
ans += count(pos + 1, currSum, currRem,
currF, nonz || d, num);
}
return dp[pos][sum][rem][tight] = ans;
}
// Function to convert x into its digit vector
// and uses count() function to return the
// required count
int solve(int x)
{
vector num;
while (x) {
num.push_back(x % 10);
x /= 10;
}
reverse(num.begin(), num.end());
// Initialize dp
memset(dp, -1, sizeof(dp));
return count(0, 0, 0, 0, 0, num);
}
// Driver code
int main()
{
int L = 1, R = 100;
n = 8, m = 2;
cout << solve(R) - solve(L);
return 0;
}
Java
// Java implementation of the approach
import java.util.*;
class GFG
{
static int M = 20;
// states - position, sum, rem, tight
// sum can have values upto 162, if we
// are dealing with numbers upto 10^18
// when all 18 digits are 9, then sum
// is 18 * 9 = 162
static int dp[][][][] = new int [M][165][M][2];
// n is the sum of digits and number should
// be divisible by m
static int n, m;
// Function to return the count of
// required numbers from 0 to num
static int count(int pos, int sum, int rem, int tight,
int nonz, Vector num)
{
// Last position
if (pos == num.size())
{
if (rem == 0 && sum == n)
return 1;
return 0;
}
// If this result is already computed
// simply return it
if (dp[pos][sum][rem][tight] != -1)
return dp[pos][sum][rem][tight];
int ans = 0;
// Maximum limit upto which we can place
// digit. If tight is 1, means number has
// already become smaller so we can place
// any digit, otherwise num[pos]
int limit = (tight != 0 ? 9 : num.get(pos));
for (int d = 0; d <= limit; d++)
{
// If the current digit is zero
// and nonz is 1, we can't place it
if (d == 0 && nonz != 0)
continue;
int currSum = sum + d;
int currRem = (rem * 10 + d) % m;
int currF = (tight != 0 || (d < num.get(pos))) ? 1 : 0;
ans += count(pos + 1, currSum, currRem,
currF, (nonz != 0 || d != 0) ? 1 : 0, num);
}
return dp[pos][sum][rem][tight] = ans;
}
// Function to convert x into its digit vector
// and uses count() function to return the
// required count
static int solve(int x)
{
Vector num = new Vector();
while (x != 0)
{
num.add(x % 10);
x /= 10;
}
Collections.reverse(num);
// Initialize dp
for(int i = 0; i < M; i++)
for(int j = 0; j < 165; j++)
for(int k = 0; k < M; k++)
for(int l = 0; l < 2; l++)
dp[i][j][k][l]=-1;
return count(0, 0, 0, 0, 0, num);
}
// Driver code
public static void main(String args[])
{
int L = 1, R = 100;
n = 8; m = 2;
System.out.print( solve(R) - solve(L));
}
}
// This code is contributed by Arnab Kundu
Python3
# Python3 implementation of the approach
# Function to return the count of
# required numbers from 0 to num
def count(pos, Sum, rem, tight, nonz, num):
# Last position
if pos == len(num):
if rem == 0 and Sum == n:
return 1
return 0
# If this result is already computed
# simply return it
if dp[pos][Sum][rem][tight] != -1:
return dp[pos][Sum][rem][tight]
ans = 0
# Maximum limit upto which we can place
# digit. If tight is 1, means number has
# already become smaller so we can place
# any digit, otherwise num[pos]
if tight:
limit = 9
else:
limit = num[pos]
for d in range(0, limit + 1):
# If the current digit is zero
# and nonz is 1, we can't place it
if d == 0 and nonz:
continue
currSum = Sum + d
currRem = (rem * 10 + d) % m
currF = int(tight or (d < num[pos]))
ans += count(pos + 1, currSum, currRem,
currF, nonz or d, num)
dp[pos][Sum][rem][tight] = ans
return ans
# Function to convert x into its digit
# vector and uses count() function to
# return the required count
def solve(x):
num = []
global dp
while x > 0:
num.append(x % 10)
x //= 10
num.reverse()
# Initialize dp
dp = [[[[-1, -1] for i in range(M)]
for j in range(165)]
for k in range(M)]
return count(0, 0, 0, 0, 0, num)
# Driver code
if __name__ == "__main__":
L, R = 1, 100
# n is the sum of digits and number
# should be divisible by m
n, m, M = 8, 2, 20
# States - position, sum, rem, tight
# sum can have values upto 162, if we
# are dealing with numbers upto 10^18
# when all 18 digits are 9, then sum
# is 18 * 9 = 162
dp = []
print(solve(R) - solve(L))
# This code is contributed by Rituraj Jain
C#
// C# implementation of the approach
using System;
using System.Collections.Generic;
class GFG
{
static int M = 20;
// states - position, sum, rem, tight
// sum can have values upto 162, if we
// are dealing with numbers upto 10^18
// when all 18 digits are 9, then sum
// is 18 * 9 = 162
static int [,,,]dp = new int [M, 165, M, 2];
// n is the sum of digits and number should
// be divisible by m
static int n, m;
// Function to return the count of
// required numbers from 0 to num
static int count(int pos, int sum, int rem, int tight,
int nonz, List num)
{
// Last position
if (pos == num.Count)
{
if (rem == 0 && sum == n)
return 1;
return 0;
}
// If this result is already computed
// simply return it
if (dp[pos,sum,rem,tight] != -1)
return dp[pos,sum,rem,tight];
int ans = 0;
// Maximum limit upto which we can place
// digit. If tight is 1, means number has
// already become smaller so we can place
// any digit, otherwise num[pos]
int limit = (tight != 0 ? 9 : num[pos]);
for (int d = 0; d <= limit; d++)
{
// If the current digit is zero
// and nonz is 1, we can't place it
if (d == 0 && nonz != 0)
continue;
int currSum = sum + d;
int currRem = (rem * 10 + d) % m;
int currF = (tight != 0 || (d < num[pos])) ? 1 : 0;
ans += count(pos + 1, currSum, currRem,
currF, (nonz != 0 || d != 0) ? 1 : 0, num);
}
return dp[pos, sum, rem, tight] = ans;
}
// Function to convert x into its digit vector
// and uses count() function to return the
// required count
static int solve(int x)
{
List num = new List();
while (x != 0)
{
num.Add(x % 10);
x /= 10;
}
num.Reverse();
// Initialize dp
for(int i = 0; i < M; i++)
for(int j = 0; j < 165; j++)
for(int k = 0; k < M; k++)
for(int l = 0; l < 2; l++)
dp[i, j, k, l] = -1;
return count(0, 0, 0, 0, 0, num);
}
// Driver code
public static void Main(String []args)
{
int L = 1, R = 100;
n = 8; m = 2;
Console.Write( solve(R) - solve(L));
}
}
// This code has been contributed by 29AjayKumar
Javascript
Python3
# User Input
l, r, n, m = 1, 100, 8, 2
# Initialize Result
output = []
# Traverse through all numbers
for x in range(l, r+1):
# Check for all conditions in every number
if sum([int(k) for k in str(x)]) == n and x % m == 0 and '0' not in str(x): # Check conditions
output.append(x)
print(len(output))
# This code is contributed by mailprakashindia
输出:
4
简短的Python实现:
蟒蛇3
# User Input
l, r, n, m = 1, 100, 8, 2
# Initialize Result
output = []
# Traverse through all numbers
for x in range(l, r+1):
# Check for all conditions in every number
if sum([int(k) for k in str(x)]) == n and x % m == 0 and '0' not in str(x): # Check conditions
output.append(x)
print(len(output))
# This code is contributed by mailprakashindia
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。