📜  用正好K个非零数字和不同的奇数和计算数字

📅  最后修改于: 2021-05-14 00:31:54             🧑  作者: Mango

给定一个整数N和一个数字K ,任务是找出从0到N的总数,这些总数恰好具有K个非零数字,并且这些数字的总和应为奇数,并且该和应是不同的。 N可以大到10 ^ 18
例子:

先决条件:Digit-DP
天真的方法:
对于N的大量输入,天真的方法无法在O(N)中从0到N的所有元素进行线性遍历并计算log(n)中的位数之和,其中n是该数字中的位数。
高效方法:

  1. 我们可以使用动态编程,这是非常有用的技术,它是digit-dp来解决此问题。
  2. 因此,除了保留非零整数的记录之外,我们还保留零变量的记录,我们可以将其保留在变量K的不同索引idx上。我们可以保留的零数目可以通过将K中的位数减去K来最初找到。 N.
  3. 我们将N的所有数字都放入一个向量,即digits
  4. 现在,我们通过分析K来计算可保持在索引idx处的元素范围。
    • 假设在索引idx处,我们剩下K = 1 (一个非零值),则我们放置元素的范围是[0,j] ,其中j是由从当前索引获得的紧密值确定的上限矢量数字中的数字。
    • 如果在idx处,我们剩下K = 0 ,则我们的范围变为[1,j],因为我们不能在此处输入0。
  5. 现在,还要使用一个参数sum ,它将计算数字的数字总和,直到基本情况成功命中为止。
  6. 另外,使用了一个布尔,它将存储所有已经计算出的奇数和,因此它给出了不同的奇数和。
  7. 重复发生将是:
    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)
    其中j = digits [idx],如果紧密= 0,否则j = 9
  8. 基本情况:
    • idx = digits.size()时K == 0总和为奇数。
      我们将总和标记为true,然后返回1,否则返回0。
    • 如果idx> digits.size(),则返回0。

因此,我们创建了一个DP表,即DP [idx] [K] [tight] [sum] ,该表将存储上述重复产生的结果,并通过将其记忆到该DP表中来返回计数。
下面是上述方法的实现:

C++
// C++ program to Count the numbers having
// exactly K non-zero digits and sum
// of digits are odd and distinct.
#include 
using namespace std;
 
// To store digits of N
vector 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);
}


Java
// Java program to count the numbers having
// exactly K non-zero digits and sum
// of digits are odd and distinct.
import java.util.*;
 
class GFG{
 
// To store digits of N
static Vector digits = new Vector();
 
// visited map
static boolean []vis = new boolean[170];
 
// DP Table
static int [][][][]dp = new int[19][19][2][170];
 
// Push all the digits of N into
// digits vector
static void ConvertIntoDigit(int n)
{
    while (n > 0)
    {
        int dig = n % 10;
        digits.add(dig);
        n /= 10;
    }
    Collections.reverse(digits);
}
 
// Function returns the count
static 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 % 2 == 1)
    {
         
        // If it is not present in map,
        // mark it as true and return 1
        if (!vis[sum])
        {
            vis[sum] = true;
            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] > 0)
    {
        return dp[idx][k][tight][sum];
    }
 
    // Upper limit
    int j;
    if (idx < digits.size() && tight == 0)
    {
        j = digits.get(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 ? 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
public static void main(String[] args)
{
 
    // 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;
     
    System.out.print(solve(0, k, 0, 0));
}
}
 
// This code is contributed by amal kumar choubey


Python3
# Python3 program to Count the numbers having
# exactly K non-zero digits and sum
# of digits are odd and distinct.
  
# To store digits of N
digits = []
  
# visited map
vis = [False for i in range(170)]
  
# DP Table
dp = [[[[0 for l in range(170)] for k in range(2)] for j in range(19)] for i in range(19)]
  
# Push all the digits of N into
# digits vector
def ConvertIntoDigit(n):
 
    while (n > 0):
        dig = n % 10;
        digits.append(dig);
        n //= 10;
    digits.reverse()
     
# Function returns the count
def solve(idx, k, tight, sum):
 
    # If desired number is formed
    # whose sum is odd
    if (idx == len(digits) and k == 0 and sum % 2 == 1):
         
        # If it is not present in map,
        # mark it as true and return 1
        if (not vis[sum]):
            vis[sum] = True;
            return 1;
         
        # Sum is present in map already
        return 0;
     
    # Desired result not found
    if (idx > len(digits)):
        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
    j = 0;
    if (idx


C#
// C# program to count the numbers having
// exactly K non-zero digits and sum
// of digits are odd and distinct.
using System;
using System.Collections.Generic;
 
class GFG{
 
// To store digits of N
static List digits = new List();
 
// visited map
static bool []vis = new bool[170];
 
// DP Table
static int [,,,]dp = new int[ 19, 19, 2, 170 ];
 
// Push all the digits of N into
// digits vector
static void ConvertIntoDigit(int n)
{
    while (n > 0)
    {
        int dig = n % 10;
        digits.Add(dig);
        n /= 10;
    }
    digits.Reverse();
}
 
// Function returns the count
static int solve(int idx, int k,
                 int tight, int sum)
{
     
    // If desired number is formed
    // whose sum is odd
    if (idx == digits.Count &&
          k == 0 && sum % 2 == 1)
    {
         
        // If it is not present in map,
        // mark it as true and return 1
        if (!vis[sum])
        {
            vis[sum] = true;
            return 1;
        }
         
        // Sum is present in map already
        return 0;
    }
 
    // Desired result not found
    if (idx > digits.Count)
    {
        return 0;
    }
 
    // If that state is already calculated
    // just return that state value
    if (dp[idx, k, tight, sum] > 0)
    {
        return dp[idx, k, tight, sum];
    }
 
    // Upper limit
    int j;
    if (idx < digits.Count && 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 ? 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
public static void Main(String[] args)
{
     
    // 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.Count - k;
     
    Console.Write(solve(0, k, 0, 0));
}
}
 
// This code is contributed by amal kumar choubey


输出:
12