Грошові суми#11504

Грошові суми (Розбір)

Нам потрібно визначити, які суми можна отримати, вибравши деяку підмножину монет. Кожну монету можна:

Тому це різновид задачі Knapsack.

Нехай сума всіх монет дорівнює S = x_1 + x_2 + ... + x_n. Оскільки n \le 100 та x_i \le 1000, то S \le 100000. Це дозволяє зберігати інформацію про кожну можливу суму.

Визначимо

dp[s] = \begin{cases} 1, & \text{якщо суму } s \text{ можна отримати},\\ 0, & \text{інакше}. \end{cases}

Спочатку ми ще не використали жодної монети. Сума завжди досяжна: dp[0] = 1. Усі інші значення дорівнюють нулю. Для прискорення зберігати будемо в бітсеті.

Для кожної монети з номіналом x_i:

dp = dp | (dp << x_i)

Підраховуємо кількість встановлених бітів, крім .

Послідовно перебираємо суми від до і виводимо ті, для яких dp[s] = 1.

Реалізація алгоритму

У бітсеті будемо зберігати масив .

#define MAX 100010
bitset<MAX> mask;

Читаємо кількість монет . Встановлюємо dp[0] = 1, оскільки суму можна отримати, не використовуючи жодної монети.

scanf("%d", &n);
mask.set(0);

У змінній обчислюємо суму номіналів усіх монет.

s = 0;

У циклі послідовно обробляємо всі монети.

for (i = 0; i < n; i++)
{

Обробляємо монету з номіналом .

  scanf("%d", &w);
  s += w;
  mask |= (mask << w);
}

Повертаємо кількість встановлених бітів. Однак сума завжди досяжна, тому її необхідно виключити.

printf("%d\n", mask.count() - 1);

Виводимо всі досяжні додатні суми.

for (i = 1; i <= s; i++)
  if (mask[i]) printf("%d ", i);
printf("\n");