#include <iostream>
#include <vector>
#include <limits>
#include <algorithm>
#include <iterator>
#include <tuple>
#include <numeric>
using namespace std;
namespace {
void PrintArray(const vector<unsigned long> &a, const vector<int> &indexes, bool first) {
string s = (first ? "First" : "Second");
cout << s << "Shares: [";
unsigned long sum = 0;
for (auto i : indexes) {
sum += a[i];
cout << a[i] << ",";
}
cout << "] " << s << "TotalValue: ";
cout << sum << endl;
}
vector<int> BuilArrayByMask(unsigned long size, unsigned long mask) {
vector<int> result;
int index = 1;
while (size && mask) {
if (mask & 1) {
result.push_back(index - 1);
}
index += 1;
mask >>= 1;
--size;
}
return result;
}
unsigned long ScoreArray(const vector<unsigned long> &a, unsigned long i) {
unsigned long res = 0;
unsigned long j = 0;
while (i != 0) {
unsigned long mask = (unsigned long) 1 << j;
if (i & mask) {
res += a[j];
i ^= mask;
}
++j;
}
return res;
}
}
// Генерируем все маски длины n бит и для каждой маски (разбиения)
// считаем ее цену. Сложность O(n*2^n) Цена второго разбиения
// получается овтоматически. Дальше сравниваем разность разбиения
// и выбираем лучшее
struct SolveBruteIterative
{
void Solve(const vector<unsigned long> &a) {
auto min_diff = std::numeric_limits<unsigned long>::max();
unsigned long first_mask = 0;
unsigned long second_mask = 0;
auto total = std::accumulate(a.begin(), a.end(), (unsigned long) 0);
const auto N = a.size();
for (unsigned long i = 0; i < (1 << (N + 1)); ++i) {
long long f = ScoreArray(a, i);
long long s = total - f;
if (abs(f - s) < min_diff) {
min_diff = abs(f - s);
first_mask = i;
second_mask = ~i;
}
}
PrintArray(a, BuilArrayByMask(N, first_mask), true);
PrintArray(a, BuilArrayByMask(N, second_mask), false);
}
};
// Рекурсивно генерируем все подмножества и выбираем наилучшее разбиение
// Сложность - очевидно O(2^n)
// По сравнению с предыдущим вариантом не надо бегать по маскам для вычисления
// цены разбиения
struct SolveBruteRecursive
{
private:
struct TaskInfo {
unsigned long min_diff = std::numeric_limits<unsigned long>::max();
vector<int> first;
vector<int> second;
unsigned long total = 0;
} t;
void Solve(
const vector<unsigned long> &a,
unsigned long i,
vector<int> &left_prefix,
vector<int> &right_prefix,
unsigned long prefix_sum) {
if (i < a.size()) {
right_prefix.push_back(i);
Solve(a, i + 1, left_prefix, right_prefix, prefix_sum);
right_prefix.pop_back();
left_prefix.push_back(i);
Solve(a, i + 1, left_prefix, right_prefix, prefix_sum + a[i]);
left_prefix.pop_back();
} else {
if (abs((long) t.total - 2 * (long) prefix_sum) < t.min_diff) {
t.min_diff = (unsigned long) (abs((long) t.total - 2 * (long) prefix_sum));
t.first = left_prefix;
t.second = right_prefix;
}
}
}
public:
void Solve(const vector<unsigned long> &a) {
t.total = std::accumulate(a.begin(), a.end(), (unsigned long) 0);
vector<int> f;
vector<int> s;
Solve(a, 0, f, s, 0);
PrintArray(a, t.first, true);
PrintArray(a, t.second, false);
}
};
// Псевдополиномиальный алгоритм o(n*sum) где sum - сумма элементов массива
// Строим матрицу M(x,y) - x максимальный индекс для построения портфеля акции ценой 'y'
// используем динамику и запоминаем откуда мы пришли в текущую ячейку . Это нужно для
// постоения результирующего набора
struct SolvePseudoPolinomial {
void Solve(const vector<unsigned long> &a) {
struct Cell {
bool Reachable = false;
int share = 0;
pair<int, int> prev = {-1, -1};
};
int best_sum = std::numeric_limits<int>::max();
Cell best_cell;
auto total = std::accumulate(a.begin(), a.end(), (unsigned long) 0);
auto num = total + 1;
vector<vector<Cell>> m(a.size(), vector<Cell>(num, Cell()));
m[0][a[0]] = {true, 0, {-1, -1}};
for (int share = 1; share < a.size(); ++share) {
for (int value = 0; value < num; ++value) {
if ((share > 0) && m[share - 1][value].Reachable) {
m[share][value] = m[share - 1][value];
}
if (value == a[share]) {
m[share][value] = {true, share, {-1, -1}};
}
if (value > a[share] && m[share - 1][value - a[share]].Reachable) {
m[share][value] = {true, share, {share - 1, value - a[share]}};
} else {
continue;
}
if (abs((int)(total - 2 * value)) < abs(int(total - 2 * best_sum))) {
best_sum = value;
best_cell = m[share][value];
}
}
}
auto i = best_sum;
Cell &cell = best_cell;
vector<int> first;
vector<int> second;
// reconstruct first array
while (true) {
first.push_back(cell.share);
i -= a[cell.share];
if (!i) {
break;
}
cell = m[cell.prev.first][cell.prev.second];
}
std::reverse(first.begin(), first.end());
// reconstruct second
int j = 0;
for (int i = 0; i < a.size(); ++i) {
if (i != first[j]) {
second.push_back(i);
} else {
++j;
}
}
PrintArray(a, first, true);
PrintArray(a, second, false);
}
};
int main() {
{
cout << "######################## Test 1 #########################" << endl;
vector<unsigned long> a {1, 2, 3, 3};
SolveBruteIterative().Solve(a);
cout << endl;
SolveBruteRecursive().Solve(a);
cout << endl;
SolvePseudoPolinomial().Solve(a);
cout << endl;
}
{
cout << "######################## Test 2 #########################" << endl;
vector<unsigned long> a {5, 5, 5, 5, 20};
SolveBruteIterative().Solve(a);
cout << endl;
SolveBruteRecursive().Solve(a);
cout << endl;
SolvePseudoPolinomial().Solve(a);
cout << endl;
}
{
cout << "######################## Test 3 #########################" << endl;
vector<unsigned long> a {5, 1, 1, 9, 7, 3};
SolveBruteIterative().Solve(a);
cout << endl;
SolveBruteRecursive().Solve(a);
cout << endl;
SolvePseudoPolinomial().Solve(a);
cout << endl;
}
{
cout << "######################## Test 4 #########################" << endl;
vector<unsigned long> a {10, 16, 82, 69, 69, 53, 13, 12, 96, 23};
SolveBruteIterative().Solve(a);
cout << endl;
SolveBruteRecursive().Solve(a);
cout << endl;
SolvePseudoPolinomial().Solve(a);
cout << endl;
}
{
cout << "######################## Test 5 #########################" << endl;
vector<unsigned long> a {19, 17, 13, 9, 6};
SolveBruteIterative().Solve(a);
cout << endl;
SolveBruteRecursive().Solve(a);
cout << endl;
SolvePseudoPolinomial().Solve(a);
cout << endl;
}
return 0;
}