Count of numbers between range having only non-zero digits whose sum of digits is N and number is divisible by M
Given a range [L, R] and two positive integers N and M. The task is to count the numbers in the range containing only non-zero digits whose sum of digits is equal to N and the number is divisible by M.
Examples:
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
Prerequisites : Digit DP
Approach: Firstly, if we are able to count the required numbers up to R i.e. in the range [0, R], we can easily reach our answer in the range [L, R] by solving for from zero to R and then subtracting the answer we get after solving for from zero to L – 1. Now, we need to define the DP states.
DP States:
- Since we can consider our number as a sequence of digits, one state is the position at which we are currently in. This position can have values from 0 to 18 if we are dealing with numbers up to 1018. In each recursive call, we try to build the sequence from left to right by placing a digit from 0 to 9.
- Second state is the sum of the digits we have placed so far.
- Third state is the remainder which defines the modulus of the number we have made so far modulo M.
- Another state is the boolean variable tight which tells the number we are trying to build has already become smaller than R so that in the upcoming recursive calls we can place any digit from 0 to 9. If the number has not become smaller, the maximum limit of digit we can place is digit at the current position in R.
For the number to have only non-zero digits, we maintain a variable nonz whose value if 1 tells the first digit in the number we have placed is a non-zero digit and thus, now we can’t place any zero digit in upcoming calls. Otherwise, we can place a zero digit as a leading zero so as to make number of digits in current number smaller than number of digits in upper limit.
Below is the implementation of the above approach:
C++
// C++ implementation of the approach #include <bits/stdc++.h> 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< int > 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< int > 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<Integer> 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<Integer> num = new Vector<Integer>(); 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< int > 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< int > num = new List< int >(); 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
<script> // JavaScript implementation of the approach var 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 var dp = Array.from(Array(M), ()=>Array(165)); // n is the sum of digits and number should // be divisible by m var n, m; // Function to return the count of // required numbers from 0 to num function count( pos, sum, rem, tight, nonz, num) { // Last position if (pos == num.length) { 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]; var 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] var limit = (tight ? 9 : num[pos]); for ( var 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 ; var currSum = sum + d; var currRem = (rem * 10 + d) % m; var currF = (tight != 0 || (d < num[pos])) ? 1 : 0; ans += count(pos + 1, currSum, currRem, currF, (nonz != 0 || d != 0) ? 1 : 0, 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 function solve(x) { var num = []; while (x) { num.push(x % 10); x = parseInt(x/10); } num.reverse(); // Initialize dp for ( var i =0; i<M; i++) for ( var j =0; j<165; j++) dp[i][j] = Array.from(Array(M), ()=>Array(2).fill(-1)); return count(0, 0, 0, 0, 0, num); } // Driver code var L = 1, R = 100; n = 8, m = 2; document.write( solve(R) - solve(L)); </script> |
4
Time Complexity: O(M*M)
Auxiliary Space: O(M*M)
Short Python Implementation :
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 |
Please Login to comment...