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

时间:2015-07-12 07:51:03

标签: algorithm math

我们给出三个整数x,y和z。您必须找到其数字仅由4,5和6组成的所有数字的总和,其中十进制表示最多为x四,十进制表示最多为y五,十进制表示最多为z六

我正在使用概念Describe Here

我的代码:

// 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); 
           }

        }

但我得到的错误答案是x=1 , y=1 and z=1我得到3315而正确答案是3675

请帮我找错。

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

7 个答案:

答案 0 :(得分:4)

问题不在于您的代码,而在于您的逻辑:设S是仅由数字4,5和6组成的数字集。您想要计算SUM(S)。但是因为你只考虑这些数字的第一个数字,所以你实际上是计算SUM(S中的s,s - s%10 ^ floor(log10(s)))。

你这样做是正确的,因为

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

简而言之,您需要做的就是在下面应用用户גלעד ברקן's approach来修复您的代码。它将导致O(xyz(x + y + z))算法,并且可以通过看到SUM(i = 0到t-1,10 ^ i)=(10 ^ t - 1)来改进为O(xyz)。 )/ 9,所以在你的代码中更改一行就足够了:

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

还有一个非常简单的O(xyz)时间+空间方法,使用动态编程,只使用最少的数学和组合:让g(x,y,z)为元组(count,sum),其中count是4-5-6个数字由完全 x四个,五个和五个六个组成。 sum 是他们的总和。然后我们有以下重复:

{{1}}

我们可以将记忆添加到 g 以避免计算结果两次,从而产生多项式时间算法而无需组合见解。

gen-y-s's answer所示,您可以使用超过3位的情况,这很容易概括。对于您对数字形状有更复杂限制的情况,也可以推广。如果你想通过组合with another generic DP approach来对给定范围内的数字求和,它甚至可以推广。

编辑:还有一种直接描述函数 f(x,y,z)的方法,包含最多 x fours,y fives和z sixes。你需要包含 - 排除。例如,对于我们的计数部分

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)

总和稍微复杂一点。

答案 1 :(得分:2)

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

这种方法很简单,可以与大多数编程语言一起使用,不一定包括类似的语法糖(如果有的话)。基本步骤是:

  1. 对于给定的整数x,y和z all> = 0,为所有组合中的每一个写一个字符串,忽略从0到x出现的'4'的顺序,从0到y出现的'5'和从0到z出现'6'。 (但是,组合是按顺序生成的,以确保完整性。)

  2. 对于(1)中生成的每个字符串,生成其字符的所有唯一和非空排列。

  3. 对于(2)中产生的每个字符串排列,将其转换为整数并将其添加到总和中。

  4. Python 3整数具有无限的精度,因此无需拖动Long或BigInteger类型来改进它。

答案 2 :(得分:1)

你的逻辑几乎是正确的。您忘了每个数字都可以显示在pow的每个配置的每个位置(您的术语中为(i,j,k))。您可以通过添加额外的循环轻松修复代码:

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

或者,更好的是,感谢Niklas B.的评论:不要添加循环,只需将pow分配给

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

答案 3 :(得分:0)

编辑:我意识到帖子链接描述了相同的内容。我误认为它与几天前漂浮在SO上的类似问题有关,而这个问题完全不同。因此删除它但后来取消删除,因为它可以解释代码中的错误。

这可以作为一个复杂的 O(x y z)的组合问题来解决。

让我们将问题分成两部分:

A部分:查找包含x 4s,y 5s和z 6s的数字之和。这很简单:

  1. 让数字如下:_ _ _..._ 4 _ ... _,其中显示的4显示在10^k位置。其余数字可以(x+y+z-1)! / ((x-1)! * y! * z!)方式排列。因此,此职位中由4人贡献的总金额为4 * 10^k * (x+y+z-1)! / ((x-1)! * y! * z!),即4 * x * 10^k * (x+y+z-1)! / (x! * y! * z!)

  2. 同样,5和6也有贡献,此位置的数字总贡献为: 10^k * (x+y+z-1)! / (x! * y! * z!) * (4x + 5y + 6z)

  3. (例如,使用x=y=z=1并且在10 ^ 2位置,贡献为400*2 + 500*2 + 600*2 = 3000(根据示例)。根据计算,它是100 * 2! / (1! * 1! * 1!) * (4+5+6) = 3000。)< / p>

    1. 因此(x + y + z)数字的总贡献为:
    2. (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

      所以在上面的例子中,所有3位数字的总和应该是: 2! / (1! * 1! * 1!) * (4+5+6) * (999)/9 = 3330。 根据示例,它是:456+465+546+564+645+654 = 3330

      <强>部分-B:

      1. 与上面相同,但x y和z分别取0-x,0-y和0-z的值。这可以通过(0..x),(0..y),(0..z)端点(包括端点)中的3路嵌套循环来完成。在每次迭代中使用上面的公式

      2. 因此,对于上面的示例,我们有x:0-1,y:0-1,z:0-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)}。根据上述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

      3. 加起来为330。 在示例中,45+54+56+65+46+64 = 330

        同样对于给出15的单位。因此总和为15+330+3330=3675

        注意:

        1. 以上可以推广到链接问题和任意数量的数字(不要求数字是连续的)。如果数字中有零,则必须略微调整方法,但基本原理是相同的。

        2. 您可以使用类似的技巧来计算1到100万之间发生的7的数量等。它是一种强大的组合方法。

答案 4 :(得分:0)

python 3中的解决方案,它使用具有重复算法的置换。可以适应其他情况,因为输入是一个字符,其中键是所请求的数字,值是每个数字的计数。

算法说明:   您可以将排列看作树,其中根包含零长度数字,其子项代表1位数字,下一级别包含2位数字等。每个节点有3个子节点,表示父节点的值延长了一位数。所以该算法基本上是一个预订树步行。每个递归调用获取当前数字,并保留要添加的数字(在字典中维护,数字为键,计数为值)。它依次在字典上依次添加每个可能的数字,然后用新的数字和数字进行递归。该方法还在开头返回当前数字,然后执行所述递归。

#!/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()

答案 5 :(得分:0)

这就是你需要的! 希望它能正常工作:)

使用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;

}

答案 6 :(得分:0)

只计算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;
}