10

We are given three integers x, y and z. You have to find the sum of all numbers whose digits are made of only 4, 5 and 6, that have at most x fours in decimal representation, at most y fives in decimal representation and at most z sixes in decimal representation

I am using the concept Describe Here

My code:

// fact[i] is i!
    for(int i=0;i<=x;i++)
        for(int j=0;j<=y;j++)
            for(int k=0;k<=z;k++){

           int t = i+j+k;
           if(t==0) continue;
           long ways = fact[t-1];
           long pow = (long) Math.pow(10,t-1);
           long rep=0;
           if(i!=0){
               rep = fact[j]*fact[k];
               if(i>0) rep*=fact[i-1];

              o+= 4*pow*(ways/rep); 
           }

           if(j!=0){
               rep = fact[i]*fact[k];
               if(j>0) rep*=fact[j-1];

              o+= 5*pow*(ways/rep); 
           }

           if(k!=0){
               rep = fact[i]*fact[j];
               if(k>0) rep*=fact[k-1];

              o+= 6*pow*(ways/rep); 
           }

        }

But I am getting the wrong answer for x=1 , y=1 and z=1 i am getting 3315 while the correct answer is 3675.

Please help me find my mistake.

4+5+6+45+54+56+65+46+64+456+465+546+564+645+654=3675
Community
  • 1
  • 1
user5107486
  • 143
  • 1
  • 5
  • 1
    @Can you explain the logic u used in your code. – Sumeet Jul 12 '15 at 08:07
  • I have a different solution to this, which uses strings in JAVA. – Sumeet Jul 12 '15 at 08:14
  • 1
    @Dante i have used the concept describe in the link using permutation – user5107486 Jul 12 '15 at 08:20
  • Can you please explain it with respect to code you have posted. – Sumeet Jul 12 '15 at 08:22
  • I am counting the number of ways the a number appear at position x by using permutation and adding the result. – user5107486 Jul 12 '15 at 08:30
  • @user5107486 , can you forsake link for your full code,for example on ideone? –  Jul 12 '15 at 09:56
  • @user5107486, please look at my post. I think there are a few issues: (a) power of 10 that you take should be equal to 't' I think (b) each value (4, 5 or 6) must be multiplied by the number of times it appears (4 * i, 5*j, 6*k etc). This is from a superficial glance at the code – user1952500 Jul 12 '15 at 12:21
  • I look at the possible numbers like a tree where in each level, the nodes contain longer numbers. Each node has 3 children. The root is a zero-length number (null), its children are 4, 5, 6 (first level nodes). 4's children are 44, 45, and 46 (second level nodes), 5's children are 54, 55, 56. Etc. We continue to add children as long as we don't exhaust the available digits (so the leaf nodes are all contain x+y+z digits, and the internal nodes contain the shorter numbers). Now we only need to write the solution as a tree walk !!!! (see my answer below) – gen-y-s Jul 12 '15 at 13:36
  • 1
    Strange that none of the current answers address the OP's question, "please help me find my mistake." – גלעד ברקן Jul 12 '15 at 13:42
  • @גלעדברקן That's no fun at all! – Niklas B. Jul 12 '15 at 14:45
  • @NiklasB. :)....... – גלעד ברקן Jul 12 '15 at 14:50
  • 1
    This is related to http://stackoverflow.com/q/31285547/2336725, although that has `x=y=z=+Infinity`. – Teepeemm Jul 13 '15 at 12:27
  • @user5107486 I updated my answer to show how to easily fix your code. – גלעד ברקן Jul 13 '15 at 20:56
  • @Teepeemm This problem here is arguably much simpler than the other because we don't have to care about an upper bound. In that case you can use a slightly more complicated [DP approach](http://stackoverflow.com/questions/22394257/how-to-count-integers-between-large-a-and-b-with-a-certain-property/22394258#22394258), combined with the idea from my answer, unless of course the upper bound is small enough to employ simple brute force – Niklas B. Jul 13 '15 at 23:54

7 Answers7

5

The problem is not with your code, it's with your logic: Let S be the set of numbers consisting of only the digits 4, 5 and 6. You want to compute SUM(S). But since you're only considering the first digits of those numbers, you're in fact computing SUM(s in S, s - s % 10^floor(log10(s))).

You're doing that correctly though, because

4 + 5 + 6 + 40 + 50 + 50 + 60 + 40 + 60 + 400 + 400 
  + 500 + 500 + 600 + 600 = 3315

Long story short, all you need to do is apply user גלעד ברקן's approach below to fix your code. It will result in an O(xyz(x+y+z)) algorithm and can be improved to O(xyz) by seeing that SUM(i = 0 to t-1, 10^i) = (10^t - 1) / 9, so it's really enough to change a single line in your code:

// was: long pow = (long) Math.pow(10,t-1);
long pow = (long) (Math.pow(10,t)-1) / 9;

There is also a really simple O(xyz) time + space approach using dynamic programming that uses only a minimum of math and combinatrics: Let g(x, y, z) be tuple (count, sum) where count is the number of 4-5-6-numbers comprised of at exactly x fours, y fives and z sixes. sum is their sum. Then we have the following recurrence:

using ll=long long;
pair<ll, ll> g(int x, int y, int z) {
    if (min(x,min(y,z)) < 0)
        return {0,0};
    if (max(x,max(y,z)) == 0)
        return {1,0};
    pair<ll, ll> result(0, 0);
    for (int d: { 4, 5, 6 }) {
        auto rest = g(x - (d==4), y - (d==5), z - (d==6));
        result.first += rest.first;
        result.second += 10*rest.second + rest.first*d;
    }
    return result;
}

int main() {
    ll res = 0;
    // sum up the results for all tuples (i,j,k) with i <= x, j <= y, k <= z
    for (int i = 0; i <= x; ++i)
        for (int j = 0; j <= y; ++j)
            for (int k = 0; k <= z; ++k)
                res += g(i, j, k).second;
    cout << res << endl;
}

We can add memoization to g to avoid computing results twice, yielding a polynomial time algorithm without needing combinatoric insights.

This is easy to generalize for the case where you have more than 3 digits you can use, as demonstrated by gen-y-s's answer. It is also generalizable to cases where you have more complex restrictions on the shape of your numbers. It can even be generalized if you want to sum the numbers in a given range, by combining it with another generic DP approach.

EDIT: There's also a way to describe your function f(x, y, z) directly, the sum of 4-5-6-numbers containing at most x fours, y fives and z sixes. You need inclusion-exclusion for that. For example, for the counting part we have

c(x, y, z) = c(x-1,y,z) + c(x,y-1,z) + c(x,y,z-1) - c(x-1,y-1,z) - c(x-1,y,z-1) - c(x,y-1,z-1) + c(x-1,y-1,z-1)

It's slightly more complicated for the sums.

Community
  • 1
  • 1
Niklas B.
  • 92,950
  • 18
  • 194
  • 224
  • To understand this, I'm ignoring the sum and simply looking at the counts, because that should be simpler to understand. You're saying basically that `f(x,y,z).count == f(x-1,y,z).count + f(x,y-1,z).count + f(x,y,z-1).count`. Is this your claim - for non-zero x,y,z of course? I ask because I think that's incorrect as there is a lot of overlap between those three sets. – Aaron McDaid Jul 12 '15 at 14:05
  • @AaronMcDaid Hm you're right, it doesn't work that way. We have to drop the "smaller than or equal" and go with "equal" – Niklas B. Jul 12 '15 at 14:07
  • In simpler terms, the count for `f(1,1,0)` is 4. As is the count for `f(0,1,1)` and for `f(1,0,1)`. But the count for `f(1,1,1)` is 8, not 12, as your identity claims. – Aaron McDaid Jul 12 '15 at 14:07
  • @AaronMcDaid It should be fixed now – Niklas B. Jul 12 '15 at 14:09
  • Maybe you could call your function `g` now instead of `f`, as its meaning has changed? – Aaron McDaid Jul 12 '15 at 14:18
  • That's almost the same as my solution, except that the virtual tree visualization I explained is way more intuitive. See my solution above. Also I'm not really sure you need memorization here. – gen-y-s Jul 12 '15 at 17:59
  • @gen-y-s Yes you do, otherwise you get exponential time – Niklas B. Jul 12 '15 at 18:04
  • @gen-y-s The problem with your approach is that cur_val is part of the DP state, you can't memoize efficiently. I'm not saying this can't be fixed, but it's non-obvious to me how to make it polynomial time. For example, try computing permuteDup(6,6,6) – Niklas B. Jul 12 '15 at 18:14
  • @NiklasB. Yes the code did depend on the current_val, but I modified it so that every node just computes the sum for its own subtree, so nodes with the same number of digits are not computed twice (also added memorization via a matrix). A parent can compute its own sum by using each possible digit in turn and adding it to the adjusted sum of the appropriate child (done by multiplying child value by 10 and adding the digits to times the count of nodes in the subtree). – gen-y-s Jul 13 '15 at 10:56
  • Please see my updated answer. Isn't that an easy way to fix the OP's code? – גלעד ברקן Jul 13 '15 at 20:55
2

in Python 3:

def sumcalc(x,y,z):
  if x < 0 or y < 0 or z < 0: return -1
  import itertools
  sum = 0
  for i, j, k in itertools.product(range(x + 1), range(y + 1), range(z + 1)):
    e = (('4' * i) + ('5' * j) + ('6' * k))
    if e:
      perms = [''.join(p) for p in itertools.permutations(e)]  
      for i in set(perms): sum += int(i)
  return sum

This method is straightforward and can be used with most any programming language not necessarily including similar syntactic sugar if any. The basic steps are:

  1. For given integers x, y and z all >= 0, write one string for each of all combinations disregarding order of '4' from 0 to x occurrences with '5' from 0 to y occcurrences and with '6' from 0 to z occurrences. (However the combinations are generated in an order to ensure completeness.)

  2. For each string produced in (1) generate all unique and non-empty permutations of its characters.

  3. For each string permutation produced in (2) convert it to an integer and add it to the sum.

Python 3 integers have unlimited precision so its not necessary to drag in a Long or BigInteger type to improve it.

1

Your logic is almost correct. You just forgot that each digit can appear in each position (pow in your terms) for each configuration of (i,j,k). You can fix your code easily by adding an additional loop:

for(int i=0;i<=x;i++)
  for(int j=0;j<=y;j++)
    for(int k=0;k<=z;k++){

       int t = i+j+k;

       for (int p=0; p<t; p++){               // added loop
         long ways = fact[t-1];
         long pow = (long) Math.pow(10,p);   // changed

Or, even better, thanks to Niklas B.'s comment: instead of adding a loop just assign pow to

pow = (long) Math.pow(10,t - 1) / 9
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
0

EDIT: I realized that the post linked describes something identical. I mistook it to be linked to a similar problem floating on SO a few days ago which was solved completely differently. Hence deleted it but later undeleted as it could explain the errors in code to the OP.

This can be solved as a combinatorial problem with a complexity of O(xyz).

Let us split the problem into two parts:

Part-A: Find the sums of numbers comprising of exactly x 4s, y 5s and z 6s. This is fairly simple:

  1. Let the number be as follows: _ _ _..._ 4 _ ... _, where the 4 shown appears in the 10^k position. The rest of the numbers can be arranged in (x+y+z-1)! / ((x-1)! * y! * z!) ways. Hence the total sum contributed by 4 in this position is 4 * 10^k * (x+y+z-1)! / ((x-1)! * y! * z!) which is 4 * x * 10^k * (x+y+z-1)! / (x! * y! * z!).

  2. Likewise 5 and 6 contribute and the total contribution from digits in this position is: 10^k * (x+y+z-1)! / (x! * y! * z!) * (4x + 5y + 6z).

(For example, with x=y=z=1 and at the 10^2 position, the contribution is 400*2 + 500*2 + 600*2 = 3000 (as per the example). As per the calculation it is 100 * 2! / (1! * 1! * 1!) * (4+5+6) = 3000.)

  1. Hence the overall contribution of (x+y+z) digit numbers is:

(x+y+z-1)! / (x! * y! * z!) * (4x + 5y + 6z) * (10^0 + 10^1 + ... + 10^(x+y+z-1))

= (x+y+z-1)! / (x! * y! * z!) * (4x + 5y + 6z) * (10^(x+y+z) - 1) / 9

So in the above example the sum of all 3-digit numbers should be: 2! / (1! * 1! * 1!) * (4+5+6) * (999)/9 = 3330. As per the example it is: 456+465+546+564+645+654 = 3330.

Part-B:

  1. Do the same as above but with x y and z taking on values from 0-x, 0-y and 0-z respectively. This can be done by a 3-way nested loop in (0..x), (0..y), (0..z) end-points inclusive. In each iteration use the above formula

  2. So for the example above we have x:0-1, y:0-1, z:0-1. The possible indices are {(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)}. The sums as per the above formula for 2-digit numbers are, for example:

    (0, 1, 1): 1!/(0! * 1! * 1!) * (5+6) * 99/9 = 121 (1, 0, 1): 1!/(1! * 0! * 1!) * (4+6) * 99/9 = 110 (1, 1, 0): 1!/(1! * 1! * 0!) * (4+5) * 99/9 = 99

which add up to 330. In the example, 45+54+56+65+46+64 = 330.

Likewise for the units which gives 15. Hence the total sum is 15+330+3330=3675.

Note:

  1. The above can be generalized to the linked problem and any number of digits (doesn't require the numbers to be consecutive). If you have zeroes in the digits, the method has to be slightly tweaked but the fundamentals are the same.

  2. You could use similar techniques to figure out the number of 7's occurring from 1 to 1 million etc. It's a powerful combinatorial method.

user1952500
  • 6,611
  • 3
  • 24
  • 37
0

Solution in python 3 which uses permutation with duplicates algorithm. Can be adapted to other cases as the input is a dictionary that has keys as the requested digits, and values are the counts of each digit.

Explanation of the algorithm: You can look at the permutations as a tree, where the root contains a zero-length number, its children represent 1-digit numbers, the next level has 2-digit numbers etc. Each node has 3 children, which represent the parent node's value extended by a digit. So the algorithm is basically a pre-order tree walk. Each recursive call gets the current number, and the digits left to add (maintained in a dictionary with the digits as keys, and the counts as values). It iterates on the dictionary adding each of the possible digits in turn, and then recurses with the new number and the digits left. The method also returns the current number in the beginning, and then performs said recursion.

#!/usr/bin/env python3

import itertools
import copy

class Matrix:
  def __init__(self, dim):
    m=None
    for i in dim:
      m=[copy.deepcopy(m) for j in range(i)]
    self.mat=m
  def getVal(self, coord):
    m=self.mat
    for i in coord:
      m=m[i]
    return m
  def setVal(self, coord, val):
    m=self.mat
    l=coord.pop()
    for i in coord:
      m=m[i]
    coord.append(l)
    m[l]=val

def sumOfNumbers(digits):
  def _sumOfNumbers(counts):
    max_v=-1
    for v in counts:
      if v<0:
        return (0,0)
      elif v>max_v:
        max_v=v
    if m.getVal(counts)==None:
      c=0
      s=0
      if max_v==0:
        c=1
      else:
        for i, d in enumerate(digits.keys()):
          counts[i]-=1
          r=_sumOfNumbers(counts)
          counts[i]+=1
          c+=r[0]
          s+=r[1]*10+r[0]*d

      m.setVal(counts, (c,s))
    return m.getVal(counts)

  dim=[v+1 for v in digits.values()]
  m=Matrix(dim)
  tot_val=0
  for i in itertools.product(*map(lambda x: range(x), dim)):
    r=_sumOfNumbers(list(i))
    tot_val+=r[1]

  return tot_val

def main():
  x=1
  y=1
  z=1
  print(x,y,z)
  print(sumOfNumbers({4: x, 5: y, 6: z}))

if __name__ == "__main__":
  main()
gen-y-s
  • 871
  • 6
  • 12
  • This is the only solution here, which doesn't hard code the number of digits we use. That is, it can construct numbers from any set of digits and their counts. It also uses dynamic programming with a cache so it only computes the sum for each combination of digit quantities once, and therefore it runs in O(xyz) time. – gen-y-s Jul 13 '15 at 10:43
  • I like it. You could make the memoization slightly simpler by converting the argument to a tuple `tuple(sorted(counts.items())` and using that directly as a key in a dictionary, without the extra class – Niklas B. Jul 13 '15 at 23:45
  • thanks. why do you suggesy to use sorted in the tuple construction ? – gen-y-s Jul 14 '15 at 00:48
  • Because otherwise the order is undefined – Niklas B. Jul 14 '15 at 01:46
0

here's what you need!! hope it works correctly:)

using namespace std;

typedef long long ll;

const ll mod = 1000000007;

int main() {

int  q, a=0, b=0, c=0, x, y, z,  l, r,count=0;
long long int  sum = 0,i,n,temp;
cin >> x >> y>>z;
string xyz = "4";
for (i = 0; i>-1; i++)
{
    n = i;
    //sum = 12345620223994828225;
    //cout << sum;
    while (n > 0)
    {
        temp = n % 10;
        if
            (temp == 4)
        {
            a++;
        }
        if (temp == 5)
        {
            b++;
        }
        if (temp == 6)
        {
            c++;
        }
        count++;
        n = n / 10;

    }

    if (a <= x && b <= y && c <= z && (a + b + c) == count)
    {
        temp = i%mod;
        sum = (sum + temp) % mod;

    }
    else if ((a + b + c) > (x + y + z))
        break;
    if (count == c)
    {
        i = 4 * pow(10, c);
    }
    count = 0;
    a = 0;
    b = 0;
    c = 0;
    temp = 0;
}
cout << sum+4;

return 0;

}

Siddhant
  • 66
  • 1
  • 9
0

Just count the no of occurances of 4,5 and 6 and store that in the second variable using memoization.. C++ code below

#include <bits/stdc++.h>
#define ll int
#define mod 1000000007
using namespace std;
struct p
{
    ll f,s;
}dp[102][102][102]={0};

p c(ll x,ll y,ll z)
{
    if (min(x,min(y,z)) < 0)
    {
        p temp;
        temp.f=temp.s=0;
        return temp;
    }
    if (!max(x,max(y,z)))
    {
        p temp;
        temp.f=1;
        temp.s=0;
        return temp;
    }
    if(dp[x][y][z].f&&dp[x][y][z].s) return dp[x][y][z];
    p ans;
    ans.f=ans.s=0;
    for (int i=4;i<7;i++)
    {
        p temp;
        if(i==4) temp=c(x-1, y, z);
        if(i==5) temp=c(x, y-1, z);
        if(i==6) temp=c(x, y, z-1);
        ans.f = (ans.f+temp.f)%mod;
        ans.s = ((long long)ans.s+((long long)i)*(long long)(temp.f) + 10*(long long)temp.s)%mod;
    }
    dp[x][y][z].f=ans.f;
    dp[x][y][z].s=ans.s;
  return ans;
}

int main()
{
    ll x,y,z,ans=0;
    scanf("%d%d%d",&x,&y,&z);
    for (ll i = 0; i <= x; ++i)
    {
        for (ll j = 0; j <= y; ++j)
        {
            for (ll k = 0; k <= z; ++k)
            {
               ans = (ans + c(i, j, k).s)%mod;
               cout<<dp[i][j][k].f<<" "<<dp[i][j][k].s<<endl;
            }
        }
    }
    printf("%d",ans);
  return 0;
}
Meghraj
  • 11
  • 2