我们给出三个整数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
答案 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)
:
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
这种方法很简单,可以与大多数编程语言一起使用,不一定包括类似的语法糖(如果有的话)。基本步骤是:
对于给定的整数x,y和z all> = 0,为所有组合中的每一个写一个字符串,忽略从0到x出现的'4'的顺序,从0到y出现的'5'和从0到z出现'6'。 (但是,组合是按顺序生成的,以确保完整性。)
对于(1)中生成的每个字符串,生成其字符的所有唯一和非空排列。
对于(2)中产生的每个字符串排列,将其转换为整数并将其添加到总和中。
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的数字之和。这很简单:
让数字如下:_ _ _..._ 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!)
。
同样,5和6也有贡献,此位置的数字总贡献为:
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
。)< / p>
(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:强>
与上面相同,但x y和z分别取0-x,0-y和0-z的值。这可以通过(0..x),(0..y),(0..z)端点(包括端点)中的3路嵌套循环来完成。在每次迭代中使用上面的公式
因此,对于上面的示例,我们有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到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;
}