Soma de subconjunto com partição igual
Reformule o problema de partição como uma mochila 0/1 com alvo igual à metade da soma total, detectando a viabilidade com um vetor booleano de DP.
Soma de subconjunto com partição igual é uma aula grátis de DSA Interview Prep no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de DSA Interview Prep, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de DSA Interview Prep inclui 4 aulas no total.
Enunciado do problema
Dado um vetor não vazio de inteiros positivos nums, determine se é possível particioná-lo em dois subconjuntos com soma igual. Por exemplo, [1, 5, 11, 5] pode ser particionado em [1, 5, 5] e [11], ambos com soma 11. Se a soma total for ímpar, a resposta será imediatamente False. Caso contrário, precisamos encontrar um subconjunto cuja soma seja total_sum // 2 — um problema clássico de soma de subconjuntos.
Redução para Soma de Subconjuntos
A redução fundamental: se a soma total S for par e um subconjunto somar S//2, os elementos restantes automaticamente também somarão S//2. Portanto, Partição de Soma de Subconjuntos Iguais reduz-se a: algum subconjunto de números soma S//2? Esse é o problema clássico Soma de Subconjuntos, que é NP-completo e resolvemos com DP de mochila 0/1 em 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?Vetor Booleano de DP
Defina um vetor booleano dp[c] em que dp[c] = True significa que existe um subconjunto cuja soma é exatamente c. Inicialize dp[0] = True (o subconjunto vazio soma 0) e todos os demais valores como False. Para cada número num, percorra a capacidade de target até num (iteração reversa da mochila 0/1) e defina 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])) # FalseAcompanhamento do Exemplo
Para [1, 5, 11, 5], total=22, alvo=11. Inicialmente, dp[0]=True. Depois de num=1: dp[1]=True. Depois de num=5: dp[5]=True, dp[6]=True. Depois de num=11: dp[11]=True (usando apenas 11). Já encontramos dp[11]=True — mas continuamos processando todos os números. Resposta final: dp[11]=True, portanto a partição é possível.
Otimização por Encerramento Antecipado
Podemos adicionar uma saída antecipada: se dp[target] se tornar True em qualquer momento, retorne True imediatamente. Isso pode acelerar drasticamente os cenários de melhor caso. Além disso, se algum elemento for igual ao alvo, podemos retornar True imediatamente. Se algum elemento exceder o alvo, ele não poderá fazer parte de nenhum subconjunto cuja soma seja o alvo, mas ainda precisamos verificar os demais.
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])) # TrueUsando um Conjunto do Python em vez de um Vetor de DP
Uma alternativa é manter um conjunto de somas alcançáveis. Comece com {0}. Para cada número, adicione-o a cada soma do conjunto atual: reachable = reachable | {s + num for s in reachable}. Filtre para manter apenas as somas que não ultrapassem o alvo. Ao final, verifique se target está no conjunto. Essa abordagem é intuitiva, mas pode usar mais memória e talvez seja mais lenta na prática.
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])) # TrueAnálise de Complexidade
A abordagem de DP é executada em tempo O(n × S), em que S = sum(nums), e usa espaço O(S) para o vetor booleano. Para as restrições do LeetCode (n ≤ 200, sum ≤ 20.000), isso representa no máximo 4.000.000 de operações — muito rápido. A abordagem com conjunto tem a mesma complexidade assintótica, mas pode ser mais lenta na prática devido à sobrecarga da construção do conjunto.
Generalização: Contagem de Subconjuntos com Determinada Soma
Um problema relacionado: conte o número de subconjuntos cuja soma seja um alvo. Altere o DP de booleano para inteiro: dp[c] = number of ways to reach sum c. Use adição em vez de OR: dp[c] += dp[c - num]. Inicialize dp[0] = 1. Use a mesma iteração reversa. Essa generalização mostra como o modelo de mochila se adapta a diferentes perguntas sobre subconjuntos.
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))Perguntas Comuns de Acompanhamento em Entrevistas
Espere perguntas de acompanhamento: (1) E se for necessário retornar a partição propriamente dita? — isso exige DP 2D para reconstrução. (2) E se os elementos puderem ser negativos? — desloque o alvo ou use um dicionário em vez de um vetor. (3) Qual é a complexidade de tempo? — O(n × soma). (4) É possível melhorar se muitos números forem iguais? — sim, use contagem de frequências para reduzir o número de iterações externas. Mencione sempre essas compensações de forma proativa.
Relação com a Mochila 0/1
Partição de Soma de Subconjuntos Iguais é uma aplicação direta da mochila 0/1: os itens são os números, os pesos são iguais aos valores e a capacidade da mochila é igual ao alvo. Perguntamos se o valor máximo é igual ao alvo (viabilidade), não qual é o valor máximo. A iteração reversa é a mesma; apenas a operação muda de max para o booleano or. Reconhecer essa relação em uma entrevista demonstra forte capacidade de reconhecer padrões.
Casos Extremos
Casos extremos a tratar: (1) vetor de comprimento 1 — um único elemento não pode ser dividido, portanto é sempre False; (2) todos os elementos idênticos e quantidade par — pode funcionar ou não, dependendo dos valores individuais; (3) somas muito grandes — verifique as restrições antes de alocar o vetor de DP; (4) elementos maiores que o alvo — podem ser ignorados (nunca poderão fazer parte de um subconjunto cuja soma seja o alvo). A verificação do maior elemento como saída antecipada trata o caso (4) com eficiência.
Verificação Rápida
Teste sua compreensão dos conceitos de Estruturas de Dados & Algoritmos — Preparação para Entrevistas de Programação desta lição.
Recapitulação da Lição
Nesta lição, você aprendeu: Partição de Soma de Subconjuntos Iguais reduz-se ao problema da soma de subconjuntos com alvo = total//2, o DP 1D booleano dp[c] usa iteração reversa idêntica à da mochila 0/1 e a abordagem generaliza-se para contar subconjuntos, substituindo o OR booleano por adição de inteiros. Em seguida, abordaremos Soma-alvo, transformando atribuições de sinais em um problema de mochila baseado na diferença entre somas de subconjuntos.
Perguntas Frequentes
A aula “Soma de subconjunto com partição igual” é grátis?
Sim — o texto completo de “Soma de subconjunto com partição igual” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de DSA Interview Prep, atualize para CoddyKit PRO. O curso de DSA Interview Prep inclui 4 aulas no total.
O que vou aprender em “Soma de subconjunto com partição igual”?
Reformule o problema de partição como uma mochila 0/1 com alvo igual à metade da soma total, detectando a viabilidade com um vetor booleano de DP. Você pratica DSA Interview Prep com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar DSA Interview Prep?
Nenhuma experiência prévia é necessária. DSA Interview Prep no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.
Quanto tempo leva a aula “Soma de subconjunto com partição igual”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de DSA Interview Prep?
Sim. Cada aula de DSA Interview Prep inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Mochila 0/1 e otimização de espaço
- Mochila ilimitada e troco de moedas II
- Soma de subconjunto com partição igual
- Soma-alvo com sinais positivos e negativos