Pul məbləğləri#11504

Pul məbləğləri (Həll təhlili)

Sikkələrin bir alt çoxluğunu seçməklə hansı cəmlərin əldə edilə biləcəyini müəyyən etməliyik. Hər bir sikkə:

Buna görə də bu Knapsack məsələsinin bir növüdür.

Bütün sikkələrin cəmi S = x_1 + x_2 + ... + x_n olsun. n \le 100 və x_i \le 1000 olduğundan, S \le 100000. Bu, hər bir mümkün cəm haqqında məlumatı saxlamağa imkan verir.

Aşağıdakı kimi təyin edək:

dp[s] = \begin{cases} 1, & \text{əgər } s \text{ cəmi əldə edilə bilirsə},\\ 0, & \text{əks halda}. \end{cases}

Başlanğıcda biz hələ heç bir sikkə istifadə etməmişik. cəmi həmişə əldə edilə bilər: dp[0] = 1. Bütün digər dəyərlər sıfırdır. Sürətləndirmək üçün -ni bitset şəklində saxlayacağıq.

Nominalı x_i olan hər bir sikkə üçün:

dp = dp | (dp << x_i)

-dan başqa qurulmuş bitlərin sayını hesablayırıq.

-dən -ə qədər olan cəmləri ardıcıl olaraq gəzib dp[s] = 1 olanları çap edirik.

Alqoritmin realizasiyası

bitsetində massivini saxlayacağıq.

#define MAX 100010
bitset<MAX> mask;

Sikkələrin sayı -i oxuyuruq. dp[0] = 1 təyin edirik, çünki heç bir sikkə istifadə etmədən cəmi əldə edilə bilər.

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

dəyişənində bütün sikkələrin nominallarının cəmini hesablayırıq.

s = 0;

Dövrdə bütün sikkələri ardıcıl olaraq emal edirik.

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

Nominalı olan sikkəni emal edirik.

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

Qurulmuş bitlərin sayını qaytarırıq. Lakin cəmi həmişə əldə edilə bildiyindən, onu istisna etmək lazımdır.

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

Bütün əldə edilə bilən müsbət cəmləri çap edirik.

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