问题:“一种算法,用于查找六位数字的数字,其中前三位数的总和等于后三位数的总和。”
我在一次采访中遇到了这个问题,想知道最好的解决方案。这就是我现在所拥有的。
方法1:当然,蛮力解决方案是检查每个数字(在100,000和999,999之间),前三位和后三位的总和是否相等。如果是,则递增某个计数器,该计数器保持所有这些数字的计数。
但这会检查所有900,000个数字,因此效率低下。
方法2:由于我们被问到“有多少”这样的数字而不是“哪个数字”,我们可以做得更好。将数字分为两部分:前三个数字(从100到999)和后三个数字(从000到999)。因此,候选数字的任一部分中的三个数字的总和可以在1到27的范围内
*为每个部分保持std::map<int, int>
,其中键是总和,值是相应部分中具有该总和的数字(3位数)。
*现在,对于第一部分中的每个数字,找出它的总和并更新相应的地图
*同样,我们可以获得第二部分的更新地图。
*现在通过乘以相应的对(例如,键4的映射1中的值和键4的映射2中的值)并将它们相加,我们得到答案。
在这种方法中,我们最终检查1K数字。
我的问题是我们如何进一步优化?有更好的解决方案吗?
答案 0 :(得分:6)
对于0 <= s <= 18
,有10 - |s - 9|
种方法可以获得s
两位数的总和。
所以,第一部分
int first[28] = {0};
for(int s = 0; s <= 18; ++s) {
int c = 10 - (s < 9 ? (9 - s) : (s - 9));
for(int d = 1; d <= 9; ++d) {
first[s+d] += c;
}
}
这是19 * 9 = 171次迭代,对于后半部分,类似地做,内部循环从0开始而不是1,即19 * 10 = 190次迭代。然后将first[i]*second[i]
与1 <= i <= 27
相加。
答案 1 :(得分:1)
生成所有三位数字;根据它们的数字总和将它们分成几组。 (实际上,你需要做的就是保持一个计算集合大小的向量)。对于每个集合,可以生成的六位数字的数量是集合平方的大小。总结设定大小的方块以获得答案。
int sumCounts[28]; // sums can go from 0 through 27
for (int i = 0; i < 1000; ++i) {
sumCounts[sumOfDigits(i)]++;
}
int total = 0;
for (int i = 0; i < 28; ++i) {
count = sumCounts[i];
total += count * count;
}
编辑用于消除计数前导零的变化:
int sumCounts[28];
int sumCounts2[28];
for (int i = 0; i < 100; ++i) {
int s = sumOfDigits(i);
sumCounts[s]++;
sumCounts2[s]++;
}
for (int i = 100; i < 1000; ++i) {
sumCounts[sumOfDigits(i)]++;
}
int total = 0;
for (int i = 0; i < 28; ++i) {
count = sumCounts[i];
total += (count - sumCounts2[i]) * count;
}
答案 2 :(得分:1)
Python实施
def equal_digit_sums():
dists = {}
for i in range(1000):
digits = [int(d) for d in str(i)]
dsum = sum(digits)
if dsum not in dists:
dists[dsum] = [0,0]
dists[dsum][0 if len(digits) == 3 else 1] += 1
def prod(dsum):
t = dists[dsum]
return (t[0]+t[1])*t[0]
return sum(prod(dsum) for dsum in dists)
print(equal_digit_sums())
结果:50412
答案 3 :(得分:0)
一个想法:对于0到27之间的每个数字,计算具有该数字总和的三位数字的数量。使用DP风格的方法应该可以有效地实现这一目标。
现在你只是对结果的平方进行求和,因为对于每个答案,你可以用一个六位数字来表示每一个。
答案 4 :(得分:0)
假设不允许前导0,您想要计算有多少种不同的方式与3个数字相加n。要计算你可以在for循环中有一个for循环。所以:
firstHalf = 0
for i in xrange(max(1,n/3),min(9,n+1)): #first digit
for j in xrange((n-i)/2,min(9,n-i+1)): #second digit
firstHalf +=1 #Will only be one possible third digit
secondHalf = firstHalf + max(0,10-|n-9|)
如果您要对数字求和,则始终唯一确定最后一个数字。因此,在第一个数字为0的情况下,我们只计算第二个数字可能有多少个不同的值。如果n小于10,则这将是n + 1.如果n更大,则直到18,它将是19-n。超过18岁,没有办法形成总和。 如果你遍历所有n,1到27,你将获得总和。