0Pricing
DSA Interview Prep · Lezione

Somma di sottoinsiemi con partizione equa

Riformuli il problema della partizione come uno 0/1 knapsack con obiettivo pari alla metà della somma totale, verificando la fattibilità con un array DP booleano.

Somma di sottoinsiemi con partizione equa è una lezione DSA Interview Prep gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento DSA Interview Prep, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso DSA Interview Prep include 4 lezioni in totale.

Definizione del problema

Dato un array non vuoto di interi positivi nums, stabilisca se è possibile suddividerlo in due sottoinsiemi con somma uguale. Ad esempio, [1, 5, 11, 5] può essere suddiviso in [1, 5, 5] e [11], entrambi con somma pari a 11. Se la somma totale è dispari, la risposta è immediatamente False. Altrimenti, è necessario trovare un sottoinsieme la cui somma sia total_sum // 2: un classico problema di subset sum.

Riduzione a Subset Sum

La riduzione chiave: se la somma totale S è pari e un sottoinsieme ha somma S//2, anche gli elementi rimanenti hanno automaticamente somma S//2. Quindi Partition Equal Subset Sum si riduce alla domanda: esiste un sottoinsieme di nums la cui somma è S//2? Questo è il classico problema NP-completo Subset Sum, che risolviamo con la programmazione dinamica dello zaino 0/1 in tempo O(n × S).

def canPartition(nums):
    total = sum(nums)
    if total % 2 != 0:
        return False  # odd sum: impossible
    target = total // 2
    # Now: does any subset of nums sum to target?

Array booleano per la DP

Definisca un array booleano dp[c], dove dp[c] = True significa che esiste un sottoinsieme con somma esattamente pari a c. Inizializzi dp[0] = True (il sottoinsieme vuoto ha somma 0) e tutti gli altri valori a False. Per ogni numero num, iteri sulla capacità da target fino a num (iterazione all'indietro dello zaino 0/1) e imposti dp[c] = dp[c] or dp[c - num].

def canPartition(nums):
    total = sum(nums)
    if total % 2 != 0:
        return False
    target = total // 2
    
    dp = [False] * (target + 1)
    dp[0] = True
    
    for num in nums:
        for c in range(target, num - 1, -1):  # backward: 0/1 knapsack
            dp[c] = dp[c] or dp[c - num]
    
    return dp[target]

print(canPartition([1, 5, 11, 5]))  # True
print(canPartition([1, 2, 3, 5]))   # False

Esecuzione passo per passo dell'esempio

Per [1, 5, 11, 5], total=22 e target=11. Inizialmente dp[0]=True. Dopo num=1: dp[1]=True. Dopo num=5: dp[5]=True, dp[6]=True. Dopo num=11: dp[11]=True (usando solo 11). Abbiamo già trovato dp[11]=True, ma continuiamo a elaborare tutti i numeri. Risposta finale: dp[11]=True, quindi la partizione è possibile.

Ottimizzazione con terminazione anticipata

Possiamo aggiungere un'uscita anticipata: se dp[target] diventa True in un qualsiasi momento, restituiamo immediatamente True. Questo può velocizzare notevolmente i casi migliori. Inoltre, se un singolo elemento è uguale a target, possiamo restituire subito True. Se un singolo elemento supera target, non può appartenere ad alcun sottoinsieme con somma pari a target, ma dobbiamo comunque verificare gli elementi rimanenti.

def canPartition_fast(nums):
    total = sum(nums)
    if total % 2 != 0:
        return False
    target = total // 2
    if max(nums) > target:  # any element > target makes it impossible
        return False
    
    dp = [False] * (target + 1)
    dp[0] = True
    
    for num in nums:
        for c in range(target, num - 1, -1):
            dp[c] = dp[c] or dp[c - num]
            if dp[target]:
                return True  # early exit
    
    return dp[target]

print(canPartition_fast([1, 5, 11, 5]))  # True

Uso di un set Python invece dell'array DP

Un'alternativa consiste nel mantenere un set delle somme raggiungibili. Si parte da {0}. Per ogni numero, lo si aggiunge a ogni somma presente nel set corrente: reachable = reachable | {s + num for s in reachable}. Filtri il risultato per conservare solo le somme che non superano target. Alla fine, verifichi se target appartiene al set. Questo approccio è intuitivo, ma può richiedere più memoria e risultare più lento nella pratica.

def canPartition_set(nums):
    total = sum(nums)
    if total % 2 != 0:
        return False
    target = total // 2
    
    reachable = {0}
    for num in nums:
        reachable = {s + num for s in reachable if s + num <= target} | reachable
    
    return target in reachable

print(canPartition_set([1, 5, 11, 5]))  # True

Analisi della complessità

L'approccio DP viene eseguito in O(n × S) tempo, dove S = sum(nums), e utilizza O(S) spazio per l'array booleano. Per i vincoli di LeetCode (n ≤ 200, somma ≤ 20.000), si tratta al massimo di 4.000.000 di operazioni: è molto veloce. L'approccio con il set ha la stessa complessità asintotica, ma può essere più lento nella pratica a causa del costo di costruzione dei set.

Generalizzazione: contare i sottoinsiemi con una determinata somma

Un problema correlato consiste nel contare il numero di sottoinsiemi con somma pari a un target. Si trasformi la DP da booleana a intera: dp[c] = number of ways to reach sum c. Utilizzi l'addizione al posto dell'OR: dp[c] += dp[c - num]. Inizializzi dp[0] = 1. L'iterazione all'indietro rimane invariata. Questa generalizzazione mostra come il modello dello zaino possa adattarsi a domande diverse sui sottoinsiemi.

def count_subsets(nums, target):
    dp = [0] * (target + 1)
    dp[0] = 1
    for num in nums:
        for c in range(target, num - 1, -1):
            dp[c] += dp[c - num]
    return dp[target]

print(count_subsets([1, 1, 1, 1, 1], 3))  # 10 (C(5,3))

Domande frequenti nei colloqui

Si aspetti domande di approfondimento: (1) Cosa fare se occorre restituire la partizione effettiva? — è necessaria una DP 2D per la ricostruzione. (2) Cosa fare se gli elementi possono essere negativi? — si sposti il target oppure si utilizzi un dizionario invece di un array. (3) Qual è la complessità temporale? — O(n × somma). (4) È possibile migliorare la soluzione se molti numeri sono uguali? — sì, si utilizzi il conteggio delle frequenze per ridurre il numero di iterazioni esterne. Menzioni sempre questi compromessi in modo proattivo.

Collegamento con lo zaino 0/1

Partition Equal Subset Sum è un'applicazione diretta dello zaino 0/1: gli elementi sono i numeri, i pesi sono uguali ai valori e la capacità dello zaino è uguale al target. Verifichiamo se il valore massimo è uguale al target (un problema di fattibilità), non quale sia il valore massimo. L'iterazione all'indietro è la stessa; cambia solo l'operazione, da max a or booleano. Riconoscere questo collegamento durante un colloquio dimostra una solida capacità di individuare i modelli ricorrenti.

Casi limite

Gestisca i seguenti casi limite: (1) array di lunghezza 1: un singolo elemento non può essere diviso in due parti, quindi il risultato è sempre False; (2) tutti gli elementi identici e in quantità pari: la soluzione può esistere oppure no, a seconda dei singoli valori; (3) somme molto grandi: verifichi i vincoli prima di allocare l'array DP; (4) elementi più grandi del target: possono essere ignorati, perché non possono mai far parte di un sottoinsieme con somma pari al target. Il controllo sull'elemento massimo come uscita anticipata gestisce il caso (4) in modo efficiente.

Verifica rapida

Verifichi la Sua comprensione dei concetti di Data Structures & Algorithms — Coding Interview Prep presentati in questa lezione.

Riepilogo della lezione

In questa lezione ha imparato che: Partition Equal Subset Sum si riduce a subset sum con target = total//2, la DP 1D booleana dp[c] utilizza l'iterazione all'indietro, identica a quella dello zaino 0/1 e l'approccio si generalizza al conteggio dei sottoinsiemi sostituendo l'OR booleano con l'addizione tra interi. Nel prossimo argomento affronteremo Target Sum, trasformando l'assegnazione dei segni in un problema di zaino sulla differenza tra le somme dei sottoinsiemi.

Domande Frequenti

La lezione «Somma di sottoinsiemi con partizione equa» è gratuita?

Sì — il testo completo di «Somma di sottoinsiemi con partizione equa» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso DSA Interview Prep, passa a CoddyKit PRO. Il corso DSA Interview Prep include 4 lezioni in totale.

Cosa imparerò in «Somma di sottoinsiemi con partizione equa»?

Riformuli il problema della partizione come uno 0/1 knapsack con obiettivo pari alla metà della somma totale, verificando la fattibilità con un array DP booleano. Eserciti DSA Interview Prep con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare DSA Interview Prep?

Non è richiesta alcuna esperienza precedente. DSA Interview Prep su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Somma di sottoinsiemi con partizione equa»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione DSA Interview Prep?

Sì. Ogni lezione DSA Interview Prep include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Knapsack 0/1 e ottimizzazione dello spazio
  2. Knapsack illimitato e Coin Change II
  3. Somma di sottoinsiemi con partizione equa
  4. Somma obiettivo con segni positivi e negativi
← Torna a DSA Interview Prep