繁体   English   中英

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

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

我们给出三个整数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

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

你这样做是正确的,因为

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

简而言之,您需要做的就是应用下面的用户גלעדברקן的方法来修复您的代码。 它将导致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;

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

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

我们可以向g添加memoization以避免计算结果两次,从而产生多项式时间算法而无需组合洞察。

对于你可以使用超过3位数的情况,这很容易推广,如gen-ys的回答所示 对于您对数字形状有更复杂限制的情况,也可以推广。 如果要通过将其与另一种通用DP方法相结合来给定范围内的数字求和,它甚至可以推广。

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

总和稍微复杂一些。

在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)中产生的每个字符串排列,将其转换为整数并将其添加到总和中。

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

你的逻辑几乎是正确的。 你刚才忘了,每个数字在每个位置(出现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

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

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

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

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

  1. 设数如下: _ _ _..._ 4 _ ... _ ,其中显示的4显示在10^k位置。 其余的数字可以排列在(x+y+z-1)! / ((x-1)! * y! * z!) (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!)这是4 * x * 10^k * (x+y+z-1)! / (x! * 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) 10^k * (x+y+z-1)! / (x! * y! * z!) * (4x + 5y + 6z)

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

  1. 因此,(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

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

B部分:

  1. 与上面相同,但xy和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

最多可达330 在该示例中, 45+54+56+65+46+64 = 330

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

注意:

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

  2. 您可以使用类似的技术来计算出1到100万等7的数量。这是一种强大的组合方法。

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()

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

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;

}

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