Count numbers with exactly K non-zero digits and distinct odd digit sum


Given an Integer N, and a number K, the task is to find out the total numbers from 0 to N which have exactly K non zero digits and the sum of those digits should be odd and that sum should be distinct. The number N can be as large as 10^18.

Examples:

Input : N = 10, K = 1
Output : 5
The numbers which follow the conditions are ->
1, 3, 5, 7 and 9
The digit sum of 10 that is (1+0) = 1 is also odd, but 1 is already included in our count.

Input : N = 100, K = 2
Output : 8

Prerequisites: Digit-DP



Naive Approach:
A naive approach for linear traversing in O(N) of all elements from 0 to N and calculating sum of digits in log(n) where n is the number of digits in that number will fail for large inputs of N.

Efficient Approach:

  1. We can use Dynamic Programming and it’s very useful technique that is digit-dp to solve this problem.
  2. So instead of keeping a record of non-zero integers, we keep a record of zeroes we can keep at different indices, idx in variable K. The number of zeroes we can keep can be found initially by subtracting K with the number of digits in N.
  3. We keep all the digits of N into a vector say, digits.
  4. Now, we calculate range of elements we can keep at index idx by analysing K.

    • Suppose at index idx, we are left with K = 1 (A Non-zero value), then our range to put elements is [0, j] where j is the upper bound decided by the tight value obtained from the current index of the digit from vector digits.
    • If at idx, we are left with K = 0, then our range becomes [1, j] because we can’t put in 0 there.
  5. Now, also take a parameter that is sum, which will calculate the sum of digits of a number till the base case hits successfully.
  6. Also, a boolean map is used which will store all the odd sums calculated already, so it gives distinct odd sums.
  7. The recurrence will be:

       f(idx, K, tight, sum) = $\sum_{i=0}^{j} f(idx+1, K-1, newtight, sum+i)   f(idx, K, tight, sum) = $\sum_{i=1}^{j} f(idx+1, K, newtight, sum+i)

    where j = digits[idx] if tight = 0, else j = 9

  8. Base Case:

    • When idx = digits.size(), K == 0 and sum is odd.
      We mark the sum as true and return 1 else return 0.
    • If idx > digits.size() then return 0.

So we create a DP table say DP[idx][K][tight][sum] which will store our result from the recurrence above and return the count by memoizing it to this DP table.

Below is the implementation of the above approach:

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ program to Count the numbers having
// exactly K non-zero digits and sum
// of digits are odd and distinct.
#include <bits/stdc++.h>
using namespace std;
  
// To store digits of N
vector<int> digits;
  
// visited map
bool vis[170] = { false };
  
// DP Table
int dp[19][19][2][170];
  
// Push all the digits of N into
// digits vector
void ConvertIntoDigit(int n)
{
    while (n) {
        int dig = n % 10;
        digits.push_back(dig);
        n /= 10;
    }
    reverse(digits.begin(), digits.end());
}
  
// Function returns the count
int solve(int idx, int k,
          int tight, int sum)
{
    // If desired number is formed
    // whose sum is odd
    if (idx == digits.size()
        && k == 0 && sum & 1) {
        // If it is not present in map,
        // mark it as true and return 1
        if (!vis[sum]) {
            vis[sum] = 1;
            return 1;
        }
        // Sum is present in map already
        return 0;
    }
  
    // Desired result not found
    if (idx > digits.size()) {
        return 0;
    }
  
    // If that state is already calculated
    // just return that state value
    if (dp[idx][k][tight][sum]) {
        return dp[idx][k][tight][sum];
    }
  
    // Upper limit
    int j;
    if (tight == 0) {
        j = digits[idx];
    }
    else {
        j = 9;
    }
  
    // To store the count of
    // desired numbers
    int cnt = 0;
  
    // If k is non-zero, i ranges from
    // 0 to j else [1, j]
    for (int i = (k ? 0 : 1);
         i <= j; i++) {
        int newtight = tight;
  
        if (i < j) {
            newtight = 1;
        }
  
        // If current digit is 0, decrement
        // k and recurse sum is not changed
        // as we are just adding 0 that
        // makes no difference
        if (i == 0)
            cnt += solve(idx + 1, k - 1,
                         newtight, sum);
  
        // If i is non zero, then k remains
        // unchanged and value is added to sum
        else
            cnt += solve(idx + 1, k, newtight,
                         sum + i);
    }
  
    // Memoize and return
    return dp[idx][k][tight][sum] = cnt;
}
  
// Driver code
int main()
{
  
    // K is the number of exact non-zero
    // elements to have in number
    int N, k;
    N = 169, k = 2;
  
    // break N into its digits
    ConvertIntoDigit(N);
  
    // We keep record of 0s we need to
    // place in the number
    k = digits.size() - k;
    cout << solve(0, k, 0, 0);
}

chevron_right


Output:

12

Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.




My Personal Notes arrow_drop_up

Recommended Posts:


Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.