Problem 88
Product-sum numbers
A natural number, N, that can be written as the sum and product of a given set of at least two natural numbers, {a1, a2, … , ak} is called a product-sum number: N = a1 + a2 + … + ak = a1 × a2 × … × ak.
For example, 6 = 1 + 2 + 3 = 1 × 2 × 3.
For a given set of size, k, we shall call the smallest N with this property a minimal product-sum number. The minimal product-sum numbers for sets of size, k = 2, 3, 4, 5, and 6 are as follows.
k=2: 4 = 2 × 2 = 2 + 2
k=3: 6 = 1 × 2 × 3 = 1 + 2 + 3
k=4: 8 = 1 × 1 × 2 × 4 = 1 + 1 + 2 + 4
k=5: 8 = 1 × 1 × 2 × 2 × 2 = 1 + 1 + 2 + 2 + 2
k=6: 12 = 1 × 1 × 1 × 1 × 2 × 6 = 1 + 1 + 1 + 1 + 2 + 6
Hence for 2≤k≤6, the sum of all the minimal product-sum numbers is 4+6+8+12 = 30; note that 8 is only counted once in the sum.
In fact, as the complete set of minimal product-sum numbers for 2≤k≤12 is {4, 6, 8, 12, 15, 16}, the sum is 61.
What is the sum of all the minimal product-sum numbers for 2≤k≤12000?
积和数
若自然数N能够同时表示成一组至少两个自然数{a1, a2, … , ak}的积和和,也即N = a1 + a2 + … + ak = a1 × a2 × … × ak,则N被称为积和数。
例如,6是积和数,因为6 = 1 + 2 + 3 = 1 × 2 × 3。
给定集合的规模k,我们称满足上述性质的最小N值为最小积和数。当k = 2、3、4、5、6时,最小积和数如下所示:
k=2: 4 = 2 × 2 = 2 + 2
k=3: 6 = 1 × 2 × 3 = 1 + 2 + 3
k=4: 8 = 1 × 1 × 2 × 4 = 1 + 1 + 2 + 4
k=5: 8 = 1 × 1 × 2 × 2 × 2 = 1 + 1 + 2 + 2 + 2
k=6: 12 = 1 × 1 × 1 × 1 × 2 × 6 = 1 + 1 + 1 + 1 + 2 + 6
因此,对于2≤k≤6,所有的最小积和数的和为4+6+8+12 = 30;注意8只被计算了一次。
已知对于2≤k≤12,所有最小积和数构成的集合是{4, 6, 8, 12, 15, 16},这些数的和是61。
对于2≤k≤12000,所有最小积和数的和是多少?
package projecteuler;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import junit.framework.TestCase;
public class Prj88 extends TestCase {
static final int LIMIT = 12000;
static int[] ts = new int[LIMIT];
public void testProductSumNumbers() {
Set<Integer> set = new HashSet<Integer>();
for (int k = 2; k <= LIMIT; k++) {
for (int num = k; num <= 2 * k; num++) {
if (dfs(0, 2, num, k, num, ts)) {
if (k % 1000 == 0) {
String fstr = "k=%d,num=%d";
fstr = String.format(fstr, k, num);
System.out.println(fstr);
}
set.add(num);
break;
}
}
}
int sum = 0;
for( int val : set){
sum += val;
}
System.out.println("sum=" + sum);
}
public boolean dfs(int depth, int maxDivisor, int num, int k, int rest,
int[] ts) {
assert (ts.length == k);
// 边界
if (depth > k - 1) {
return false;
}
// 检查是否可提前返回
if (depth > 0) {
int tmp = 0;
int mul = 1;
for (int i = 0; i < depth; i++) {
tmp += ts[i];
mul *= ts[i];
}
assert (ts[depth] <= (num / mul));
if (tmp + (k - depth - 1) + num / mul == num) {
ts[depth] = num / tmp;
printResult(ts, k, depth, num, false);
return true;
}
}
List<Integer> divisors = getDivisors(rest, maxDivisor);
for (int i = 0; i < divisors.size(); i++) {
int div = divisors.get(i);
// 保证因子递增且剩下的余积不小于当前最大因子
if (rest / div < div) {
continue;
}
ts[depth] = div;
if (dfs(depth + 1, div, num, k, rest / div, ts)) {
return true;
}
// no need to reset; a depth flag is enough
}
return false;
}
void printResult(int[] idx, int K, int numOfDiv, int num, boolean print) {
if (!print) {
return;
}
for (int i = 0; i < idx.length; i++) {
if (i != idx.length - 1) {
if (i <= numOfDiv) {
System.out.print(idx[i] + "+");
} else {
System.out.print(1 + "+");
}
} else {
if (i <= numOfDiv) {
System.out.print(idx[i] + "=");
} else {
System.out.print(1 + "=");
}
}
}
System.out.println(num);
}
List<Integer> getDivisors(int num, int start) {
List<Integer> ret = new ArrayList<Integer>();
assert (start <= num);
for (int i = start; i < num; i++) {
if (num % i == 0) {
ret.add(i);
}
}
return ret;
}
}