Subset Sum — problem podskupa sa zadatom sumom i detaljno objašnjenje sa DP rešenjem
Šta je Subset Sum?
Dat je skup celih brojeva S = {s₁, s₂, ..., sₙ} i ciljana suma T.
Pitanje glasi: Postoji li podskup elemenata iz S čija suma iznosi tačno T?
Svaki element skupa može biti ili uključen u podskup ili ne, dakle rešenje se bira među svim mogućim kombinacijama elemenata.
Primer: S = {3, 1, 5, 9, 12}, T = 9 → odgovor: da (jedan mogući podskup je {3,1,5}).
Opis zadatka
Na osnovu skupa S i ciljne sume T, potrebno je utvrditi:
- da li postoji podskup koji sumira tačno T,
- po želji, pronaći jedan ili sve podskupove koji zadovoljavaju ovu sumu.
Svaki element može biti uključen najviše jednom. Podskup može biti prazan ako T = 0.
Moguće varijante problema
- Decision problem: Vraća da/ne odgovor da li postoji podskup sa sumom T.
- Counting problem: Izbrojati koliko različitih podskupova daje sumu T.
- Construction problem: Pronaći i vratiti jedan ili sve podskupove sa sumom T.
- Optimizacija: Naći podskup čija suma je što bliža T ako T nije moguće tačno postići.
Primer sa konkretnim brojevima
Ulaz:
- Skup: S = {3, 1, 5, 9, 12}
- Ciljna suma: T = 9
Objašnjenje:
- Mogući podskupovi koji daju sumu 9: {3,1,5}, {9}
- Svaki element se uzima ili ne uzima, nema deljenja ili višestrukog uzimanja istog elementa.
Izlaz:
- Postoji podskup sa sumom T? → da
- Primer jednog podskupa: {3,1,5}
Napomene i preporuke za rešavanje
- Ovo je klasičan NP-kompletan problem u opštem slučaju, ali se često rešava dinamičkim programiranjem kada su brojevi nenegativni i T nije prevelik.
- Efikasni pristupi uključuju:
- Top-down rekurziju sa memoizacijom (proverava se svaki podproblem samo jednom),
- Bottom-up tabulaciju (popunjavanje DP tabele od 0 do T),
- Bitset optimizaciju za male/umerene T vrednosti,
- Meet-in-the-middle pristup za veći broj elemenata (n ≤ 40).
- Za negativne brojeve problem postaje složeniji i zahteva dodatne tehnike (pomak suma, ili hash mape).
Ovaj problem je odlična vežba za razumevanje dinamičkog programiranja i preklapajućih podproblema.
Brute force (zašto nije dovoljno)
Najjednostavniji pristup isproba sve podskupove — ima ih 2ⁿ.
To je prihvatljivo samo za male n (n ≤ 20). Potrebno je efikasnije rešenje.
Dinamičko programiranje — osnovna ideja
Definišemo logički (boolean) DP niz:
dp[sum] = true ako je moguća suma 'sum' korišćenjem već obrađenih elemenata
Početno: dp[0] = true (prazan podskup daje sumu 0), sve ostale dp[x] su inicijalno false.
Kada obradimo novi broj num, ažuriramo dp tako da uključimo sve nove sume koje dobijamo dodavanjem num na već ostvarene sume.
for (int num : S) {
for (int sum = T; sum >= num; --sum) {
dp[sum] = dp[sum] || dp[sum - num];
}
}
Zašto iteriramo unazad (od T prema num)?
- Ako bismo iterirali unapred (od num ka T), tada bi nova vrednost
dp[x]mogla uticati na veće sume u istoj iteraciji i omogućiti da isti element bude upotrebljen više puta — što menja problem u unbounded (neograničeni) knapsack. - Iteracijom unazad garantujemo da se nova suma
dp[sum]dobija isključivo na osnovu starih (prethodnih) stanjadp[sum - num], dakle iste vrednosti element ne koristi više puta.
Intuitivna objašnjenja i analogije (kockice / tegovi)
Analogia: Zamislimo da su elementi težine kockica ili tegova. Imamo neke tegove, svaki sa svojom težinom. dp[x] = true znači "u tvom ruksaku možeš formirati težinu x".
- Ako imaš samo teg težine 3, možeš dobiti težine: 0 (prazan ruksak) i 3 (uzmeš taj teg). Sve ostale težine su nemoguće. - Kada kasnije dobiješ još jedan teg, recimo 4, sada možeš napraviti i 4 (samo taj teg) i 7 (3+4). - Važno je da zapamtiš sve dosadašnje moguće težine (dp[vrednosti]) jer će uskoro novi tegovi možda dozvoliti kombinovanje sa baš tim vrednostima.
Detaljni korak-po-korak primeri
Primer A — samo jedan element: {3}, cilj T = 6
Početno: dp[0] = true, ostalo false.
Nakon obrade 3:
| sum | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| dp | true | false | false | true | false | false | false |
Objašnjenje: možemo ili ne uzeti teg 3 → dobijamo 0 ili 3. 1 i 2 su nemoguće jer nemamo kockicu manjeg iznosa.
Primer B — dodajemo drugi element: {3, 4}, cilj T = 7
Stanje pre drugog elementa (posle 3): dp[0]=T, dp[3]=T.
Obrađujemo num = 4. Novi mogući iznosi nastaju tako što dodamo 4 na sve prethodne istinite vrednosti:
- 0 + 4 = 4 → dp[4] = true
- 3 + 4 = 7 → dp[7] = true
| sum | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| dp | true | false | false | true | true | false | false | true |
Zaključak: dp[5] ostaje false jer da bi 5 bilo moguće trebalo bi pre toga da postoji dp[1] = true (tada 1+4=5). Pošto dp[1] = false, nemamo 5.
Primer C — dodajemo treći element: {3, 4, 2}, cilj T = 6
Posle prethodnih koraka, dp[4]=true i dp[3]=true. Sada obradimo num = 2 (iteriramo sum unazad):
- dp[6] |= dp[4] → dp[6] = true (jer dp[4] = true i 4+2 = 6)
- dp[5] |= dp[3] → dp[5] = true (jer dp[3] = true i 3+2 = 5)
- dp[2] |= dp[0] → dp[2] = true
Stanje konačno (do 6): dp[6]=true → cilj dostignut.
Zašto pamtimo sve dp[1..T]?
Čuvamo sve međurezultate jer ne znamo koji će se elementi pojaviti kasnije. Svaki sledeći broj može kombinovati bilo koju već ostvarenu sumu. Ako ne sačuvamo dp[3] danas, ne bismo mogli kasnije formirati 3+2=5. Dakle, i one sume koje izgleda da trenutno "ne pomažu" mogu biti ključne za buduće korake.
Zašto se ide unazad (detaljno, još jednom)
Koristimo jednodimenzionalni dp niz radi memorijske optimizacije. Ako iteriramo sumove unapred (od num ka T), novoizračunate vrednosti u istoj iteraciji mogu biti ponovno upotrebljene, što znači da bi isti element bio iskorišćen više puta (neželjeno). Iteracijom unazad dp[sum] zavisi samo od starih vrednosti dp[sum-num], što osigurava da je svaki element uključen najviše jednom.
Implementacija (C++)
Efikasna implementacija koristeći O(T) memorije:
// Subset Sum (boolean) -- O(n * T) vreme, O(T) memorije
#include <iostream>
#include <vector>
using namespace std;
bool subsetSum(const vector<int>& S, int T) {
vector<bool> dp(T+1, false);
dp[0] = true;
for (int num : S) {
for (int sum = T; sum >= num; --sum) {
dp[sum] = dp[sum] || dp[sum - num];
}
}
return dp[T];
}
int main() {
vector<int> S = {3, 4, 2};
int T = 6;
cout << (subsetSum(S, T) ? "Da\\n" : "Ne\\n");
return 0;
}
Komentari uz kod
dp[sum]označava da li je suma moguća koristeći obrađene elemente.- Petlja
for (int sum = T; sum >= num; --sum)osigurava da novi element ne bude iskorišćen više puta. - Na kraju funkcije vraćamo
dp[T]— da li je ciljna suma moguća.
Složenost
- Vremenska složenost: O(n · T)
- Memorija: O(T) (može se i dodatno optimizovati u specijalnim slučajevima)
Saveti i česte greške
- Ne iterirajte unapred (od
numkaT) — to vodi u grešku “korišćenje elementa više puta”. - Zapamti da
dp[0]uvek budetrue(prazan podskup). - Ako su brojevi veliki ili ako T veoma raste, DP može postati spor ili memorijski zahtevan — tada potraži alternativne pristupe ili optimizacije (npr. bitset optimizaciju, meet-in-the-middle za n ≤ 40, ili heuristike).
- Za brojanje načina (koliko podskupova daje sumu T) logika je slična, ali treba čuvati brojeve (long long) i paziti na prelivanje / modulo ako je potrebno.
Kratki rezime (za učenika)
- Definiši
dp[sum]— da li je suma moguća. - Počni sa
dp[0] = true, ostalo false. - Za svaki broj
numažuriraj dp unazad: zasum = T..numuradidp[sum] |= dp[sum-num]. - Na kraju proveri
dp[T].
Vizuelna DP tabela (korak-po-korak)
Primer: S = {3, 4, 2}, ciljna suma T = 6.
Svaki red predstavlja stanje dp[] nakon obrade jednog broja.
| korak | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| početno | T | F | F | F | F | F | F |
| posle 3 | T | F | F | T | F | F | F |
| posle 4 | T | F | F | T | T | F | T |
| posle 2 | T | F | T | T | T | T | T |
Iz vizuelne tabele se jasno vidi kako svaka nova vrednost num omogućava nove tačne sume, i kako se
dp[] postupno "popunjava" sve dok ne dostignemo cilj T = 6.
Intuitivna vizuelna ilustracija — kockice / tegovi
Problem Subset Sum možemo posmatrati kao slaganje tegova na levu stranu vage. Imamo dostupne tegove (brojeve), i želimo da vidimo koje sve težine možemo napraviti njihovim kombinovanjem.
Primer: S = {3, 4, 2}, ciljna suma T = 6.
Korak 1: Početno stanje
Na početku možemo napraviti samo sumu 0 jer nismo stavili nijedan teg.
Korak 2: Dodajemo teg 3
Jedina nova suma koju možemo formirati je 3, zato što imamo 0, i 0 + 3 = 3.
Korak 3: Dodajemo teg 4
Sada dobijamo nove moguće sume:
- 4 (jer 0 + 4 = 4)
- 7 (0 + 3 + 4 = 7, ali > T=6, ignorišemo za ciljnu sumu)
Korak 4: Dodajemo teg 2
Teg 2 donosi još nekoliko mogućih suma:
- 2 (0 + 2)
- 5 (3 + 2)
- 6 (4 + 2)
Sad možemo formirati apsolutno sve sume do 6, uključujući i ciljnu sumu T=6.
Zaključak
Vizuelni model sa tegovima pomaže da se shvati ključna ideja: sve sume koje ćemo možda trebati kasnije moraju biti zapamćene u dp[] dok gradimo rešenja. Na ovaj način vidimo kako dodavanje svakog elementa „gradi“ nove moguće sume na osnovu prethodnih.
Rekonstrukcija podskupa – koji brojevi formiraju koju sumu?
Kada radimo Subset Sum samo sa boolean DP nizom dp[sum], znamo da li je neka suma moguća, ali ne znamo kojim brojevima.
Zato pravimo posebnu strukturu koja pamti:
parent[sum] = (prethodna_suma, broj_koji_je_dodat)
Ovo nam omogućava da “idemo unazad”, od ciljne sume T, pa pratimo koji broj ju je formirao i koja suma je postojala pre njega.
Primer
Skup: {3, 4, 5}
Ciljna suma: T = 7
Kako se popunjava parent tabela:
| Suma | dp[suma] | parent[suma] | Objašnjenje |
|---|---|---|---|
| 3 | true | (0, 3) | 3 se može formirati direktno brojem 3 |
| 4 | true | (0, 4) | 4 se može formirati direktno brojem 4 |
| 5 | true | (0, 5) | 5 se može formirati direktno brojem 5 |
| 7 | true | (3, 4) | 7 = 3 + 4 → koristi se suma 3, koju smo ranije formirali |
□ Rekonstrukcija podskupa za T = 7
Start: 7 parent[7] = (3, 4) → znači uzeli smo 4 parent[3] = (0, 3) → znači uzeli smo 3 parent[0] = kraj
Rešenje:
C++ kod za praćenje podskupa
vector<bool> dp(T+1, false);
vector<pair<int,int>> parent(T+1, {-1,-1});
dp[0] = true;
for (int num : a) {
for (int sum = T; sum >= num; --sum) {
if (dp[sum - num] && !dp[sum]) {
dp[sum] = true;
parent[sum] = {sum - num, num};
}
}
}
// Rekonstrukcija
vector<int> subset;
int cur = T;
while (cur != 0 && parent[cur].first != -1) {
subset.push_back(parent[cur].second);
cur = parent[cur].first;
}
Vizuelna ilustracija rekonstrukcije
7 ⬑ dodali smo 4
3 ⬑ dodali smo 3
0 (početak)
To znači da je DP algoritam pronašao da se 7 formira lančano:
Tekst zadatka — Subset Sum
Dat je niz od n pozitivnih celih brojeva nums[] i ciljna suma
T.
Potrebno je odrediti da li postoji podskup elemenata niza (svaki element se
može koristiti najviše jednom) čija je suma elemenata tačno jednaka
T.
Ako takav podskup postoji, ispisati YES, u suprotnom ispisati
NO.
Ulaz
- Prva linija sadrži dva cela broja
niT - Druga linija sadrži
ncelih pozitivnih brojeva — elemente niza
Izlaz
- Ispisati
YESako postoji podskup sa sumomT - U suprotnom ispisati
NO
Napomena
- Svaki element niza može se koristiti najviše jednom
- Zadatak zahteva samo proveru postojanja, ne i ispis samog podskupa
Rešenje zadatka — Subset Sum (DP sa memoizacijom)
U ovom delu nalazi se kompletno uputstvo i potpuno objašnjeno rešenje za zadatak „Subset Sum“. Ideja je da se produbi razumevanje rekurentnih odnosa, memoizacije i tabulacije, koji spadaju u osnovne tehnike dinamičkog programiranja.
NP‑kompleksnost i ograničenja DP rešenja
Subset Sum problem je poznat kao NP‑kompletan u opštem slučaju.
Vikipedija objašnjava detalje.
To znači da, iako DP rešenje radi u vremenu O(n·T) (gde je n broj elemenata, a T ciljna suma), ovo je pseudopolynomialno.
Pseudopolynomialno znači da vreme izvršavanja zavisi od vrednosti brojeva, a ne samo od njihove količine.
Za male brojeve i umerene ciljne sume, DP je vrlo efikasan.
Međutim, ako su brojevi i T veliki, složenost može postati nepraktična, i DP ne garantuje polinomsko vreme u opštem slučaju.
Alternativa za veće vrednosti i optimizacija
Kada se susretnemo sa velikim brojevima ili velikim T, postoje naprednije tehnike:
- Meet-in-the-middle algoritam — deli skup na dve polovine i kombinuje njihove sume; ima složenost
O(2^{n/2}), često praktično zan ≈ 40. - Heuristike i aproksimacije — za probleme gde precizno rešenje nije neophodno, omogućavaju brža rešenja.
- Pseudopolynomialna optimizacija — neke varijante DP mogu smanjiti memoriju ili vreme, ali i dalje zavise od vrednosti brojeva.
Za dodatne detalje i formalne rezultate pogledajte arXiv: Pseudopolynomial Subset Sum algoritmi.
Zaključak: DP rešenje Subset Sum problema je vrlo korisno, ali učenici i takmičari treba da budu svesni njegovih ograničenja — nije univerzalno brzo za sve unose.
Vizuelna ilustracija pseudopolynomialnog rasta DP za Subset Sum
Sledeća tabela pokazuje primer kako DP niz dp[0..T] raste kada povećavamo ciljne sume i dodajemo brojeve.
Boje označavaju da li je suma moguća (zelena) ili ne (crvena).
Ovo ilustruje zašto DP postaje nepraktičan kada su brojevi veliki.
| Brojevi | T = 0 | T = 1 | T = 2 | T = 3 | T = 4 | T = 5 | T = 6 |
|---|---|---|---|---|---|---|---|
| Inicijalno | ✔ | ✖ | ✖ | ✖ | ✖ | ✖ | ✖ |
| + Broj 1 | ✔ | ✔ | ✖ | ✖ | ✖ | ✖ | ✖ |
| + Broj 3 | ✔ | ✔ | ✖ | ✔ | ✖ | ✖ | ✔ |
| + Broj 4 | ✔ | ✔ | ✖ | ✔ | ✔ | ✔ | ✔ |
Legenda: ✔ suma je moguća sa trenutnim podskupom, ✖ suma nije moguća.
Ova tabela vizuelno pokazuje kako dodavanje brojeva omogućava sve veći broj ciljeva, ali takođe ilustruje rast memorije sa T.
Kada su T i elementi veliki (npr. T > 10⁵), DP niz postaje ogroman — iako je rešenje tačno, nije praktično.
Subset Sum — Vizuelna ilustracija podskupova (kockice/tegovi)
Sledeća ilustracija prikazuje kako različiti podskupovi brojeva mogu da formiraju određene ciljne sume T.
Svaka “kockica” predstavlja broj iz skupa, a redovi pokazuju kombinacije koje doprinose sumama.
Primer skupa: nums = [1, 3, 4], cilj T = 6
| Podskup | Sum = 0 | Sum = 1 | Sum = 2 | Sum = 3 | Sum = 4 | Sum = 5 | Sum = 6 |
|---|---|---|---|---|---|---|---|
| { } | ✔ | ✖ | ✖ | ✖ | ✖ | ✖ | ✖ |
| {1} | ✔ | ✔ | ✖ | ✖ | ✖ | ✖ | ✖ |
| {3} | ✔ | ✖ | ✖ | ✔ | ✖ | ✖ | ✖ |
| {4} | ✔ | ✖ | ✖ | ✖ | ✔ | ✖ | ✖ |
| {1,3} | ✔ | ✔ | ✖ | ✔ | ✖ | ✔ | ✖ |
| {1,4} | ✔ | ✔ | ✖ | ✖ | ✔ | ✔ | ✖ |
| {3,4} | ✔ | ✖ | ✖ | ✔ | ✔ | ✖ | ✔ |
| {1,3,4} | ✔ | ✔ | ✖ | ✔ | ✔ | ✔ | ✔ |
Legenda: ✔ suma je moguća sa datim podskupom, ✖ suma nije moguća.
Svaka linija pokazuje kako kombinacije elemenata doprinose formiranju ciljne sume T.
Ova ilustracija pomaže da se intuitivno shvati zašto DP popunjava dp[sum] polje od nule do T i kako se prethodno zapamćeni rezultati kombinuju.
Subset Sum — Rekonstrukcija podskupa
Često u praktičnim zadacima nije dovoljno samo utvrditi da li je ciljna suma T moguća.
Potrebno je i pronaći jedan podskup koji čini tu sumu.
Ovo se može uraditi tako što pored DP tabele čuvamo i informaciju o tome da li je neki element
zaista morao biti izabran.
Ideja rešenja
- Kreiramo DP tabelu
dp[i][sum]koja označava da li je sumasummoguća koristeći prvihielemenata. - Pomoćna tabela
take[i][sum]se postavlja natruesamo ako je uzimanje elementa jedini način da se dobije ta suma. - Posle popunjavanja DP tabele, počinjemo od
dp[n][T]i vraćamo se unazad, koristećitakeda rekonstrušemo jedan validan podskup.
C++ kod — rekonstrukcija podskupa
// Subset Sum sa rekonstrukcijom jednog podskupa
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, T;
cin >> n >> T;
vector<int> nums(n);
for(int i = 0; i < n; i++) cin >> nums[i];
vector<vector<bool>> dp(n+1, vector<bool>(T+1, false));
vector<vector<bool>> take(n+1, vector<bool>(T+1, false));
dp[0][0] = true;
for(int i = 1; i <= n; i++) {
for(int sum = 0; sum <= T; sum++) {
// slučaj: ne uzimamo nums[i-1]
if(dp[i-1][sum]) {
dp[i][sum] = true;
}
// slučaj: uzimamo nums[i-1] (ali samo ako je to jedini način)
if(sum >= nums[i-1] && dp[i-1][sum - nums[i-1]] && !dp[i-1][sum]) {
dp[i][sum] = true;
take[i][sum] = true;
}
}
}
if(!dp[n][T]) {
cout << "Nema resenja\n";
return 0;
}
// rekonstrukcija podskupa
vector<int> subset;
int sum = T;
for(int i = n; i > 0; i--) {
if(take[i][sum]) {
subset.push_back(nums[i-1]);
sum -= nums[i-1];
}
}
cout << "Podskup koji daje sumu " << T << ": ";
for(int x : subset) cout << x << " ";
cout << "\n";
return 0;
}
Test primeri i granični slučajevi
- Primer gde postoji rešenje:
nums = [1, 3, 4], T = 5→ podskup:{1,4}. - Primer gde nema rešenja:
nums = [2,5,7], T = 4→ ispisujeNema resenja. - Primer gde je T veće od sume svih elemenata:
nums = [1,2,3], T = 10→ nema rešenja. - Primer sa duplikatima:
nums = [1,2,2,3], T = 4→ podskup može biti{2,2}ili{1,3}.
Ova implementacija garantuje da je rekonstruisani podskup uvek u skladu sa DP tabelom.
Matrica take se koristi isključivo za odluke koje su bile neophodne, čime se izbegavaju
nekonzistentne rekonstrukcije.
Kako funkcioniše rekonstrukcija podskupa (vizuelno objašnjenje)
Nakon što je DP tabela dp popunjena, znamo da li je ciljna suma T dostižna,
ali još uvek ne znamo koji elementi su korišćeni.
Zato koristimo pomoćnu tabelu take.
Ključna ideja je sledeća:
take[i][sum] = trueznači da se sumasumne može dobiti bez uzimanja elementanums[i-1].
Ako je suma mogla da se dobije i bez tog elementa, take[i][sum] ostaje false,
čak i ako je uzimanje takođe bilo moguće.
Primer
Neka su dati brojevi:
nums = [1, 3, 4]
T = 5
DP tabela (1 = moguće)
suma →
i ↓ 0 1 2 3 4 5
-------------------
0 1 0 0 0 0 0
1 (1) 1 1 0 0 0 0
2 (3) 1 1 0 1 1 0
3 (4) 1 1 0 1 1 1
Označene take odluke
Pogledajmo kako se dobija dp[3][5]:
-
Bez broja
4:dp[2][5] = false -
Sa brojem
4:dp[2][1] = true
Pošto ne postoji rešenje bez broja 4, postavljamo:
take[3][5] = true
Vizuelno kretanje unazad (rekonstrukcija)
Start: (i=3, sum=5)
|
|-- take[3][5] = true → uzimamo 4
|
(i=2, sum=1)
|
|-- take[2][1] = false → ne uzimamo 3
|
(i=1, sum=1)
|
|-- take[1][1] = true → uzimamo 1
|
(i=0, sum=0) ✓
Rekonstruisani podskup je:
{1, 4}
Zašto ovo uvek radi?
takese postavlja samo kada je odluka bila neizbežna.- Tokom rekonstrukcije nikada ne ulazimo u stanje koje DP tabela ne podržava.
- Uvek dobijamo validan podskup, bez kontradikcija.
Ovaj pristup daje jasan i pouzdan način da se, pored informacije da li rešenje postoji, dobije i konkretan primer tog rešenja.
Varijante zadatka problema podskupa sa zadatom sumom(SubsetSum engl.)
1. Subset Sum — osnovni problem (YES / NO)
Tekst zadatka
Dat je niz nums[ ] od n pozitivnih celih brojeva i ciljna suma
T. Potrebno je utvrditi da li postoji podskup elemenata niza čija
suma iznosi tačno T. Svaki element se može koristiti najviše jednom.
Ako postoji — ispisati YES, inače NO.
Primer
Ulaz:
5 9
3 34 4 12 5
Izlaz:
YES
Objašnjenje:
Postoji podskup {4, 5} koji sabiranjem daje 9.
2. Subset Sum — rekonstrukcija rešenja
Tekst zadatka
Pored odgovora YES/NO, vratiti i konkretan podskup indeksa elemenata koji daju sumu
T. Ako postoji više rešenja, vratiti bilo koje jedno.
Ako ne postoji — ispisati NO.
Primer
Ulaz:
5 9
3 34 4 12 5
Izlaz:
YES
Izabrani indeksi (1-based): 3 5
Objašnjenje:
nums[3]=4, nums[5]=5 → 4+5=9
3. Subset Sum — koliko različitih podskupova daje sumu T?
Tekst zadatka
Izračunati broj različitih podskupova niza nums čija suma iznosi tačno T.
Ispisati broj (ako postoji mogućnost preliva, koristiti 64-bitni tip ili modulo).
Primer
Ulaz:
4 5
1 2 3 2
Izlaz:
3
Objašnjenje:
Podskupovi koji daju 5: {1,2,2} (s različitim indeksima),
{2,3} (različiti izbor indeksa za 2) — ukupno 3 načina.
4. Subset Sum — minimalan broj elemenata
Tekst zadatka
Naći najmanji broj elemenata niza nums tako da njihova suma bude tačno T.
Ako nije moguće, ispisati -1 (ili neku poruku da nema rešenja).
Primer
Ulaz:
5 9
3 34 4 12 5
Izlaz:
2
Objašnjenje:
4 + 5 = 9 → minimalni broj elemenata = 2
5. Veza sa 0/1 Knapsack problemom
Tekst zadatka / ideja
0/1 Knapsack: imamo n predmeta sa težinama w[i] i
vrednostima v[i], i kapacitet ranca W.
Cilj je da se maksimizuje zbir vrednosti bez prelaska ukupne težine W.
Povezanost: Subset Sum je poseban slučaj Knapsack problema.
Ako postavimo v[i] = w[i] i pitamo da li možemo dobiti tačno vrednost
T, dobijamo Subset Sum.
Obrnuto, Knapsack je generalizacija jer dozvoljava različite težine i vrednosti.
6. Napredne varijante Subset Sum-a
Pregled zadataka
- Subset Sum sa negativnim brojevima
- Neograničeno korišćenje elemenata (unbounded)
- Meet-in-the-middle (n ≤ 40)
- Najbliža suma ≤ T (approx / closest)
Skriveni mini kviz — Subset Sum problem
Klikni da proveriš svoje razumevanje Subset Sum algoritma i DP pristupa.
Otvori kviz
Mini kviz: Da li razumeš Subset Sum?
1. Šta predstavlja dp[i][s] u Subset Sum problemu?
2. Koji je osnovni slučaj DP-a?
3. Zašto se u optimizovanom DP-u iterira suma unazad (od T ka 0)?
4. Kakva je vremenska složenost standardnog DP rešenja za Subset Sum?
5. Kada je Subset Sum problem NP-težak u opštem slučaju?
|
Prethodno
|< DP: Najduži zajednički podniz (LCS) |
Sledeće
Zamena iteracija formulom >| |