简体   繁体   English

由4 5 6组成的数字的总和

[英]Summation of a number made up of 4 5 6

We are given three integers x, y and z. 我们给出三个整数x,y和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 你必须找到所有数字的总和,其数字仅由4,5和6组成,十进制表示最多为x四,十进制表示最多为y五,十进制表示最多为z六

I am using the concept Describe Here 我正在使用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 . 但我得到错误的答案x=1 , y=1 and z=1我得到3315而正确答案是3675

Please help me find my mistake. 请帮我找出错误。

4+5+6+45+54+56+65+46+64+456+465+546+564+645+654=3675

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). 问题不在于您的代码,而在于您的逻辑:设S是仅由数字4,5和6组成的数字集。您想要计算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))). 但是因为你只考虑这些数字的第一个数字,所以你实际上是计算SUM(s中的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: 它将导致O(xyz(x + y + z))算法,并且可以通过看到SUM(i = 0到t-1,10 ^ i)=(10 ^ t - 1)来改进为O(xyz)。 )/ 9,所以在你的代码中改变一行真的够了:

// 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. 还有一个非常简单的O(xyz)时间+空间方法,使用动态编程,只使用最少的数学和组合:让g(x,y,z)为元组(count,sum),其中count是4的数-5-6个数字由正好 x四个,五个和五个六个组成。 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. 我们可以向g添加memoization以避免计算结果两次,从而产生多项式时间算法而无需组合洞察。

This is easy to generalize for the case where you have more than 3 digits you can use, as demonstrated by gen-ys's answer . 对于你可以使用超过3位数的情况,这很容易推广,如gen-ys的回答所示 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 . 如果要通过将其与另一种通用DP方法相结合来给定范围内的数字求和,它甚至可以推广。

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. 编辑:还有一种方法可以直接描述你的函数f(x,y,z) ,4-5-6数的总和最多包含x fours,y fives和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) 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. 总和稍微复杂一些。

in Python 3: 在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. 对于给定的整数x,y和z all> = 0,为所有组合中的每一个写入一个字符串,忽略从0到x出现的'4'的顺序,从0到y出现的'5'和从0到z的'6'发生。 (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. 对于(1)中产生的每个字符串,生成其字符的所有唯一和非空排列。

  3. For each string permutation produced in (2) convert it to an integer and add it to the sum. 对于(2)中产生的每个字符串排列,将其转换为整数并将其添加到总和中。

Python 3 integers have unlimited precision so its not necessary to drag in a Long or BigInteger type to improve it. Python 3整数具有无限的精度,因此无需拖动Long或BigInteger类型来改进它。

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) . 你刚才忘了,每个数字在每个位置(出现pow的每个配置在计算) (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 或者,甚至更好,感谢Niklas B.的评论:而不是添加一个循环,只需分配pow

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

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. 我误认为它与几天前漂浮在SO上的类似问题有关,而这个问题完全不同。 Hence deleted it but later undeleted as it could explain the errors in code to the OP. 因此删除了它,但后来取消删除,因为它可以解释OP中代码中的错误。

This can be solved as a combinatorial problem with a complexity of O(x y z) . 这可以作为具有O(x y z)复杂度的组合问题来解决。

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. A部分:求出包含正好x 4s,y 5s和z 6s的数字之和。 This is fairly simple: 这很简单:

  1. Let the number be as follows: _ _ _..._ 4 _ ... _ , where the 4 shown appears in the 10^k position. 设数如下: _ _ _..._ 4 _ ... _ ,其中显示的4显示在10^k位置。 The rest of the numbers can be arranged in (x+y+z-1)! / ((x-1)! * y! * z!) 其余的数字可以排列在(x+y+z-1)! / ((x-1)! * y! * z!) (x+y+z-1)! / ((x-1)! * y! * z!) ways. (x+y+z-1)! / ((x-1)! * y! * z!)方式。 Hence the total sum contributed by 4 in this position is 4 * 10^k * (x+y+z-1)! / ((x-1)! * y! * z!) 因此,在这个位置贡献4的总和是4 * 10^k * (x+y+z-1)! / ((x-1)! * y! * z!) 4 * 10^k * (x+y+z-1)! / ((x-1)! * y! * z!) which is 4 * x * 10^k * (x+y+z-1)! / (x! * y! * z!) 4 * 10^k * (x+y+z-1)! / ((x-1)! * y! * z!)这是4 * x * 10^k * (x+y+z-1)! / (x! * y! * z!) 4 * x * 10^k * (x+y+z-1)! / (x! * y! * z!) . 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) 同样地,5和6贡献,并且该位置中的数字的总贡献是: 10^k * (x+y+z-1)! / (x! * y! * z!) * (4x + 5y + 6z) 10^k * (x+y+z-1)! / (x! * y! * z!) * (4x + 5y + 6z) . 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 .) (例如,当x=y=z=1并且在10 ^ 2位置时,贡献是400*2 + 500*2 + 600*2 = 3000 (根据示例)。根据计算,它是100 * 2! / (1! * 1! * 1!) * (4+5+6) = 3000

  1. Hence the overall contribution of (x+y+z) digit numbers is: 因此,(x + y + z)数字的总体贡献是:

(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 所以在上面的例子中,所有3位数字的总和应该是: 2! / (1! * 1! * 1!) * (4+5+6) * (999)/9 = 3330 2! / (1! * 1! * 1!) * (4+5+6) * (999)/9 = 3330 . 2! / (1! * 1! * 1!) * (4+5+6) * (999)/9 = 3330 As per the example it is: 456+465+546+564+645+654 = 3330 . 根据示例,它是: 456+465+546+564+645+654 = 3330

Part-B: B部分:

  1. Do the same as above but with xy and z taking on values from 0-x, 0-y and 0-z respectively. 与上面相同,但xy和z分别取0-x,0-y和0-z的值。 This can be done by a 3-way nested loop in (0..x), (0..y), (0..z) end-points inclusive. 这可以通过(0..x),(0..y),(0..z)端点(包括端点)中的3路嵌套循环来完成。 In each iteration use the above formula 在每次迭代中使用上面的公式

  2. So for the example above we have x:0-1, y:0-1, z:0-1. 因此,对于上面的示例,我们有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)} . 可能的索引是{(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: 根据上述2位数字公式的总和,例如:

    (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 (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 . 最多可达330 In the example, 45+54+56+65+46+64 = 330 . 在该示例中, 45+54+56+65+46+64 = 330

Likewise for the units which gives 15. Hence the total sum is 15+330+3330=3675 . 同样,对于给出15的单位。因此总和是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. 您可以使用类似的技术来计算出1到100万等7的数量。这是一种强大的组合方法。

Solution in python 3 which uses permutation with duplicates algorithm. python 3中的解决方案,它使用具有重复算法的排列。 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. 算法解释:您可以将排列看作树,其中根包含零长度数,其子代表1位数字,下一级有2位数字等。每个节点有3个子节点,其中表示由数字扩展的父节点的值。 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()

here's what you need!! 这就是你需要的! hope it works correctly:) 希望它正常工作:)

using namespace std; using namespace std;

typedef long long ll; typedef long long ll;

const ll mod = 1000000007; const ll mod = 1000000007;

int main() { 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;

} }

Just count the no of occurances of 4,5 and 6 and store that in the second variable using memoization.. C++ code below 只计算4,5和6的出现次数,并使用memoization将其存储在第二个变量中。下面是C ++代码

#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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM