Balanced Partition
National Olympiad — Regional round (2023 season)
Submission name: min.cpp
Problem summary
Partition the supplied positive integers into two disjoint groups minimizing the absolute difference of their sums. Treat equal input occurrences as separate elements.
Input
n, then n positive integers.
Output
The smallest possible absolute sum difference.
Constraints
1 < n < 300; 0 < a[i] < 10000.
Worked example
An independently prepared example for this explanation.
Input
5 7 3 10 5 7
Output
2
Solution analysis
- If the total is T and one group sums to s, the difference is |T−2*s|. By swapping the groups, we may require s≤T/2, so choose the largest attainable subset sum not exceeding T/2.
- Represent attainable sums as bits. Initially only bit zero is set. For an element v, update reachable |= reachable << v. The shift appends this element to every old subset, while OR preserves the subsets omitting it.
- In the sample, T=32. Sum 16 is unattainable, but 15 is attained by 10+5. The other group is 7+3+7=17, giving the optimal difference 2.
Why it works
After processing each element, a subset either excludes that occurrence and keeps an old sum, or includes it and adds its value to an old sum. The bitset update describes exactly these possibilities, so induction proves reachability is exact. For s≤T/2, T−2*s decreases as s increases, proving that the largest reachable such s is optimal.
Watch out. Use each input occurrence at most once. The sample repeats 7 despite calling the input a set; preserve every occurrence. Do not use a greedy split.
Commented C++17 solution
An independently written complete program with standard headers.
#include<iostream>
#include<vector>
#include<bitset>
// Algorithm: If the total is T and one group sums to s, the difference is |T−2*s|.
using namespace std;
int main(){
int n;
cin>>n;
vector<int>values(n);
int total=0;
for(int&value:values){
cin>>value;
total+=value;
}
bitset<3000001>reachable;
reachable[0]=true;
for(int value:values){
reachable|=reachable<<value;
}
for(int sum=total/2;sum>=0;--sum){
if(reachable[sum]){
cout<<total-2*sum<<'\n';
break;
}
}
}