All Euler problems
Project Euler

Special Subset Sums: Optimum

Let S(A) denote the sum of elements in a set A of positive integers. We call A a special sum set if for any two non-empty disjoint subsets B, C subseteq A: 1. S(B)!= S(C) (distinct subset sums). 2....

Source sync May 21, 2026
Problem #0103
Level Level 05
Solved By 9,450
Languages C++, Python
Answer 20313839404245
Length 488 words
linear_algebraconstructiveoptimization

Problem Statement

This archive keeps the full statement, math, and original media on the page.

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:

1.
\(S(B) \neq S(C)\); that is, sums of subsets cannot be equal.
2.
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. \begin {align*} 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\} \end {align*}

It seems that for a given optimum set, \(A = \{a_1, a_2, \dots , a_n\}\), the next optimum set is of the form \(B = \{b, a_1 + b, a_2 + b, \dots , a_n + 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.

Problem 103: Special Subset Sums: Optimum

Mathematical Development

Theorem 1 (Equivalent Condition for Property 2). Let A={a1<a2<⋯<an}A = \{a_1 < a_2 < \cdots < a_n\} be a set of positive integers. Property 2 holds if and only if

∑i=1k+1ai>∑i=n−k+1naifor all k=1,2,…,⌊n/2⌋.\sum_{i=1}^{k+1} a_i > \sum_{i=n-k+1}^{n} a_i \qquad \text{for all } k = 1, 2, \ldots, \lfloor n/2 \rfloor.

Proof. (⇒\Rightarrow) Take B={a1,…,ak+1}B = \{a_1, \ldots, a_{k+1}\} and C={an−k+1,…,an}C = \{a_{n-k+1}, \ldots, a_n\}. These are disjoint since k+1≤⌊n/2⌋+1≤n−k+1k + 1 \leq \lfloor n/2 \rfloor + 1 \leq n - k + 1 for k≤⌊n/2⌋k \leq \lfloor n/2 \rfloor. Since ∣B∣=k+1>k=∣C∣|B| = k + 1 > k = |C|, Property 2 yields S(B)>S(C)S(B) > S(C).

(⇐\Leftarrow) Let B,CB, C be disjoint subsets of AA with ∣B∣=m>ℓ=∣C∣|B| = m > \ell = |C|. Write B={b1<⋯<bm}B = \{b_1 < \cdots < b_m\} and C={c1<⋯<cℓ}C = \{c_1 < \cdots < c_\ell\}. Since the bib_i are mm distinct elements of AA, we have bi≥aib_i \geq a_i, so S(B)≥∑i=1maiS(B) \geq \sum_{i=1}^{m} a_i. Similarly, since the cjc_j are ℓ\ell distinct elements of AA, we have cj≤an−ℓ+jc_j \leq a_{n - \ell + j}, so S(C)≤∑j=n−ℓ+1najS(C) \leq \sum_{j=n-\ell+1}^{n} a_j. Applying the hypothesis with k=ℓk = \ell:

S(B)≥∑i=1mai≥∑i=1ℓ+1ai>∑j=n−ℓ+1naj≥S(C).■S(B) \geq \sum_{i=1}^{m} a_i \geq \sum_{i=1}^{\ell+1} a_i > \sum_{j=n-\ell+1}^{n} a_j \geq S(C). \qquad \blacksquare

Theorem 2 (Reduction to Global Subset-Sum Uniqueness). Property 1 holds for AA if and only if all 2n−12^n - 1 non-empty subset sums of AA are pairwise distinct.

Proof. (⇐\Leftarrow) If all subset sums are distinct, then in particular S(B)≠S(C)S(B) \neq S(C) for any two distinct non-empty subsets, a fortiori for disjoint ones.

(⇒\Rightarrow) We prove the contrapositive. Suppose distinct non-empty subsets B′,C′B', C' satisfy S(B′)=S(C′)S(B') = S(C'). Let D=B′∩C′D = B' \cap C', B=B′∖DB = B' \setminus D, C=C′∖DC = C' \setminus D. Then BB and CC are disjoint. Since B′≠C′B' \neq C', at least one of B,CB, C is non-empty. If both are non-empty, then

S(B)=S(B′)−S(D)=S(C′)−S(D)=S(C),S(B) = S(B') - S(D) = S(C') - S(D) = S(C),

violating Property 1. If exactly one (say CC) is empty, then C′⊆B′C' \subseteq B' and S(B′)=S(C′)S(B') = S(C') forces S(B′∖C′)=0S(B' \setminus C') = 0; but all elements are positive, so B′∖C′=∅B' \setminus C' = \emptyset, contradicting B′≠C′B' \neq C'. ■\blacksquare

Lemma 1 (Near-Optimum Heuristic Construction). Given an optimal special sum set {b1,…,bn−1}\{b_1, \ldots, b_{n-1}\} of size n−1n - 1 with middle element bmb_m where m=⌈(n−1)/2⌉m = \lceil(n-1)/2\rceil, the set {bm, bm+b1, bm+b2, …, bm+bn−1}\{b_m,\, b_m + b_1,\, b_m + b_2,\, \ldots,\, b_m + b_{n-1}\} is a near-optimum special sum set of size nn.

Proof. This construction, due to Lunnon, yields a set satisfying both properties. The minimum element is bm>0b_m > 0, ensuring positivity. Property 2 is satisfied because the spread bm+b1b_m + b_1 vs.\ bm+bn−1b_m + b_{n-1} inherits the structure of the original set. Optimality is not guaranteed, but the set serves as a starting point for local search. ■\blacksquare

Editorial

The near-optimum set from the construction theorem gives a narrow neighborhood in which the true optimum is expected to lie, so the implementation performs a local search around {20,31,38,39,40,42,45}\{20,31,38,39,40,42,45\} rather than scanning arbitrary 7-element sets. Every candidate produced by a small perturbation is sorted and immediately rejected if it contains a nonpositive entry, repeats an element, or already has a larger total sum than the best set found so far.

The two special-set conditions are then applied in the cheapest useful order. Property 2 is a fast filter because it only compares a few prefix and suffix sums. Only candidates that survive that test pay the cost of Property 1, where every non-empty subset sum is generated and checked for collisions. Whenever a candidate passes both tests and improves the current optimum, it becomes the new best set.

Pseudocode

Begin with the near-optimum set as the current best answer.

For every 7-tuple of perturbations in the chosen search window:
    Add the perturbation to the base set and sort the result.
    Skip this candidate if an entry is nonpositive, if two entries coincide, or if its total already exceeds the best sum found so far.
    Test Property 2 by comparing the sums of the smallest k+1 elements with the largest k elements.
    If Property 2 holds, enumerate all non-empty subsets and check that no subset sum repeats.
    If the candidate passes both tests and improves the best set, record it.

Concatenate the elements of the best set and return that string.

Complexity Analysis

  • Time. The perturbation space has (2⋅3+1)7=77=823,543(2 \cdot 3 + 1)^7 = 7^7 = 823{,}543 candidates. For each candidate passing the sum bound and Property 2 filter, Property 1 costs O(2n)O(2^n) with n=7n = 7, i.e., 128 subset-sum computations. With aggressive pruning, total work is on the order of 10610^6 operations.
  • Space. O(2n)=O(128)O(2^n) = O(128) for the hash set of subset sums.

Answer

20313839404245\boxed{20313839404245}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_103/solution.cpp
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
#include <string>
using namespace std;

/*
 * Problem 103: Special Subset Sums: Optimum
 *
 * Search for the optimal n=7 special sum set by perturbing the
 * near-optimum {20,31,38,39,40,42,45} by +/-3 on each element.
 */

bool is_special(vector<int>& a) {
    int n = a.size();
    sort(a.begin(), a.end());
    // Property 2
    for (int k = 1; k <= n / 2; k++) {
        int lo = 0, hi = 0;
        for (int i = 0; i <= k; i++) lo += a[i];
        for (int i = 0; i < k; i++) hi += a[n - 1 - i];
        if (lo <= hi) return false;
    }
    // Property 1
    set<int> sums;
    for (int mask = 1; mask < (1 << n); mask++) {
        int s = 0;
        for (int i = 0; i < n; i++)
            if (mask & (1 << i)) s += a[i];
        if (sums.count(s)) return false;
        sums.insert(s);
    }
    return true;
}

int main() {
    int base[] = {20, 31, 38, 39, 40, 42, 45};
    int best_sum = 255;
    vector<int> best_set(base, base + 7);
    const int D = 3;

    for (int d0 = -D; d0 <= D; d0++)
    for (int d1 = -D; d1 <= D; d1++)
    for (int d2 = -D; d2 <= D; d2++)
    for (int d3 = -D; d3 <= D; d3++)
    for (int d4 = -D; d4 <= D; d4++)
    for (int d5 = -D; d5 <= D; d5++)
    for (int d6 = -D; d6 <= D; d6++) {
        vector<int> a = {
            base[0]+d0, base[1]+d1, base[2]+d2, base[3]+d3,
            base[4]+d4, base[5]+d5, base[6]+d6
        };
        bool ok = true;
        for (int x : a) if (x <= 0) { ok = false; break; }
        if (!ok) continue;

        sort(a.begin(), a.end());
        for (int i = 1; i < 7; i++)
            if (a[i] <= a[i-1]) { ok = false; break; }
        if (!ok) continue;

        int s = 0;
        for (int x : a) s += x;
        if (s > best_sum) continue;

        if (is_special(a)) {
            if (s < best_sum || (s == best_sum && a < best_set)) {
                best_sum = s;
                best_set = a;
            }
        }
    }

    sort(best_set.begin(), best_set.end());
    string ans;
    for (int x : best_set) ans += to_string(x);
    cout << ans << endl;
    return 0;
}