Special subset sums: optimum
Let S(A) represent the sum of elements in set A of size n. We shall call it a special sum set if for any two non-empty disjoint subsets, B and C, the following properties are true:
- S(B) ≠ S(C); that is, sums of subsets cannot be equal.
- If B contains more elements than C then S(B) > S(C).
If S(A) is minimised for a given n, we shall call it an optimum special sum set. The first five optimum special sum sets are given below.
n = 1: {1}
n = 2: {1, 2}
n = 3: {2, 3, 4}
n = 4: {3, 5, 6, 7}
n = 5: {6, 9, 11, 12, 13}
It seems that for a given optimum set, A = {a1, a2, … , an}, the next optimum set is of the form B = {b, a1+b, a2+b, … ,an+b}, where b is the “middle” element on the previous row.
By applying this “rule” we would expect the optimum set for n = 6 to be A = {11, 17, 20, 22, 23, 24}, with S(A) = 117. However, this is not the optimum set, as we have merely applied an algorithm to provide a near optimum set. The optimum set for n = 6 is A = {11, 18, 19, 20, 22, 25}, with S(A) = 115 and corresponding set string: 111819202225.
Given that A is an optimum special sum set for n = 7, find its set string.
NOTE: This problem is related to Problem 105 and Problem 106.
特殊的子集和:最优解
记S(A)是大小为n的集合A中所有元素的和。若任取A的任意两个非空且不相交的子集B和C都满足下列条件,我们称A是一个特殊的和集:
- S(B) ≠ S(C);也就是说,任意子集的和不相同。
- 如果B中的元素比C多,则S(B) > S(C)。
对于给定的n,我们称使得S(A)最小的集合A为最优特殊和集。前5个最优特殊和集如下所示。
n = 1: {1}
n = 2: {1, 2}
n = 3: {2, 3, 4}
n = 4: {3, 5, 6, 7}
n = 5: {6, 9, 11, 12, 13}
似乎对于一个给定的最优特殊和集A = {a1, a2, … , an},下一个最优特殊和集将是B = {b, a1+b, a2+b, … ,an+b}的形式,其中b是集合A“正中间”的元素。
应用这条“规则”,我们猜测对于n = 6的最优特殊和集将是A = {11, 17, 20, 22, 23, 24},相应的S(A) = 117。然而,事实并非如此,我们的方法仅仅只能找出近似最优特殊和集。对于n = 6,最优特殊和集是A = {11, 18, 19, 20, 22, 25},相应的S(A) = 115,对应的集合数字串是:111819202225。
若集合A是n = 7时的最优特殊和集,求其对应的集合数字串。
package projecteuler;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import junit.framework.TestCase;
public class Prj103 extends TestCase {
List<int[]> resultList = new ArrayList<int[]>();
/**
* simple dfs;
* search start from n-1;
* k + (k + 1) > k + n -1
*/
public void testSpecialSubsetSumsOptimum() {
int[] arr = new int[7];
dfs(arr, 0, arr.length - 1, 50 );
System.out.println("size=" + resultList.size());
int sum = Integer.MAX_VALUE;
int minId = 0;
for( int i = 0 ; i < resultList.size() ; i ++){
int[] _val = resultList.get(i);
int val = sumArr(_val);
if( val < sum){
sum = val;
minId = i;
}
}
printArr(resultList.get(minId));
System.out.println("sum=" + sumArr(resultList.get(minId)) );
}
public void dfs(int[] arr, int id, int start, int end) {
if (id >= arr.length) {
if (checkOk(arr)) {
resultList.add(Arrays.copyOf(arr, arr.length));
printArr(arr);
}
return;
}
for (int i = start; i <= end; i++) {
if (id == 0) {
arr[id] = i;
dfs(arr, id + 1, start, end);
} else {
if (i <= arr[id - 1]) {
continue;
}
arr[id] = i;
dfs(arr, id + 1, start, end);
}
}
}
int sumArr(int arr[]) {
int sum = 0;
for (int val : arr) {
sum += val;
}
return sum;
}
private boolean checkOk(int[] arr) {
if (Arrays.equals(arr, new int[] { 11, 18, 19, 20, 22, 25 })) {
System.out.println();
}
assert (arr.length >= 5);
int n = arr.length;
int sumAsc = arr[0] + arr[1];
int sumDesc = arr[n - 1];
int count = 3;
int c = 2;
while (true) {
if (count > n) {
break;
}
if (sumAsc <= sumDesc) {
return false;
}
sumAsc += arr[c];
sumDesc += arr[n - c];
c++;
count += 2;
}
Set<Integer> set = new HashSet<Integer>();
int sum = 0;
for (int val : arr) {
set.add(val);
sum += val;
}
set.add(sum);
for (int p = 2; p <= n / 2; p++) {
Comb cb = new Comb(n, p);
List<int[]> cbs = cb.calculateComb();
for (int i = 0; i < cbs.size() - 1; i++) {
int[] _cb = cbs.get(i);
int sum1 = 0;
int sum2 = 0;
for (int j = 0; j < _cb.length; j++) {
if (_cb[j] == 1) {
sum1 += arr[j];
} else {
sum2 += arr[j];
}
}
if (set.contains(sum1)) {
return false;
}
set.add(sum1);
if (p == n / 2 && n % 2 == 0) {
continue;
}
if (set.contains(sum2)) {
return false;
}
set.add(sum2);
}
}
return true;
}
void printArr(int[] diff) {
for (int i = 0; i < diff.length; i++) {
System.out.print(diff[i] + "\t," );
}
System.out.println();
}
/**
* 组合
*
* @author suc
*
*/
public static class Comb {
private int n;
private int p;
public Comb(int n, int p) {
this.n = n;
this.p = p;
}
public List<int[]> calculateComb() {
assert (p < n && p > 0);
List<int[]> ret = new ArrayList<int[]>();
int[] arrs = new int[n];
for (int i = 0; i < p; i++) {
arrs[i] = 1;
}
ret.add(Arrays.copyOf(arrs, arrs.length));
for (;;) {
boolean find = findNext(arrs);
ret.add(Arrays.copyOf(arrs, arrs.length));
if (!find) {
break;
}
}
return ret;
}
private boolean findNext(int[] arrs) {
int count = 0;
int find = -1;
for (int i = 0; i < arrs.length - 1; i++) {
if (arrs[i] == 1) {
count++;
}
if (arrs[i] == 1 && arrs[i + 1] == 0) {
int tmp = arrs[i + 1];
arrs[i + 1] = arrs[i];
arrs[i] = tmp;
find = i;
break;
}
}
count--;
for (int i = 0; i < find; i++) {
if (i < count) {
arrs[i] = 1;
} else {
arrs[i] = 0;
}
}
return !(find == -1);
}
}
}