Brigadeiros
Comentário escrito por João Pedro Castro
Conhecimento prévio necessário:
- Introdução à Programação Dinâmica
- Problema da Mochila - Tecnicamente opcional, mas meio impossível fazer sem ter as ideias daqui.
Subtarefa 2:
,
, 
Vamos brutar tudo (testar todas as possibilidades de algo). Primeiro, vamos brutar as posições finais, isso é
. Agora, para cada uma dessas possibilidades vamos brutar a ordem que cada um vai (se o primeiro menor das posições iniciais vai com a segunda menor das posições finais, etc), isso é
. Por fim, para cada um desses casos conseguimos calcular em
(mas até
passaria) o
mínimo para chegar nessa posição, e se
falamos que a resposta é o máximo entre a resposta atual e a soma da quantidade de brigadeiros nas posições finais. A complexidade final é
.
Dica de implementação: para brutar todas as possibilidades, você pode criar um vetor de posições que comece com
0s e depois tenha
1s. E então, usar next_permutation para ver todas as possibilidades. Em uma dada permutação desse vetor original você pode dizer que se e somente se a i-ésima posição for igual a 1 tem alguém do grupo.
Subtarefa 3:
, 
Podemos fazer a mesma estratégia da parcial anterior mas com uma observação a mais: sejam as posições iniciais do grupo , e as finais
: para conseguir o tempo mínimo de chegar nessa posição é ideal dizer que o cara na posição
vai para a posição
para todo
; ou seja, o menor vai com o menor, o segundo menor vai com o segundo menor, etc. A intuição disso é bem simples, e não é difícil "provar" (sem muita formalidade) fazendo umas contas.
Assim, conseguimos só brutar as possibilidades de escolhas, e calcular o tempo para chegar lá em
. A complexidade final é
.
Dica: o caso onde é maior é quando
, então nesse caso o pior é
, que ainda passa tranquilamente.
Subtarefa 4 e 5:
, 
Vamos usar programação dinâmica! Aqui a definição de cada parte:
- Estado: nosso estado vai ser
, onde:
significa que usamos as posições de 1 até
para as posições finais dos membros do grupo que colocamos até agora.
significa que já colocamos os
primeiros membros do grupo.
significa o tempo máximo que podemos usar para chegar na melhor soma possível.
significa a melhor soma possível que conseguimos dado
,
e
.
- Caso base: o único caso base necessário é
. O resto, se não calcularmos, pode ser visto só como
.
- Transição: vou colocar várias possibilidades, e vai ser só o máximo entre elas.
- Podemos só não botar nada em
:
.
- Podemos ficar parados sem fazer nada:
.
- Se
, podemos botar o
-ésimo (do menor para o maior em posição) membro do grupo na posição
:
(perceba que essa transição só funciona por conta da nossa observação que é sempre melhor colocar o menor com menor, segundo menor com segundo menor, etc.)
- Podemos só não botar nada em
- A resposta, dado o estado, é
.
A complexidade é .
Subtarefa 6: 
Vamos fazer a mesma DP, só que com uma observação a mais. Responda essa pergunta: se posso fazer trocas de elementos adjacentes, qual o número máximo de swaps para ordenar o vetor?
Se você já viu a técnica de bubble sort, essa pergunta tem uma resposta bem direta: é , e na verdade sempre vai ser menor que
(não convém mostrar a conta aqui). Logo, posso chegar de qualquer ordenação do vetor para qualquer outra em menos de
operações, ou seja, logo após receber o
podemos dizer que
e a resposta ainda estaria certa, pois se existe uma resposta válida fazendo mais que
trocas, também existirá uma fazendo menos que
trocas.
Assim, tendo que , temos uma complexidade de
.
Subtarefa 7: Sem restrições adicionais.
Quando você leu o enunciado, idealmente se atentou de um detalhe específico que não seria colocado sem motivo: . Ou seja, todo prato tem entre
e
brigadeiros, mas o que isso faz a gente pensar? Se você já está acostumado com problemas de DP, é direto: a soma máxima é
.
E uma técnica legal de problemas de DP, é que em diversos problemas se você tem algo como (ou em um caso geral com quantas variáveis você quiser), você pode dizer que
, ou qualquer outra troca. Ou seja, se você tem
variáveis que indicam seu estado, e mais
que indica sua resposta, você pode trocar qualquer uma das que indicam o estado com a da resposta que o estado ainda vai ser perfeitamente expressado. Claro que varia de problema a problema, e vai ter trocas melhores que outras, mas em problemas de DP sempre pense em qual a maneira com menos possibilidades de guardar esse estado.
No nosso caso, ao invés de fazer a , vamos fazer a
. Já que o máximo de
é
e o máximo de
é
. Agora a DP significa: colocando os
primeiros do grupo em algumas das
primeiras posições, qual o menor tempo
para atingir a soma
? Se for impossível podemos só dizer que
.
O estado foi explicado acima, o caso base é o mesmo, e o resto fica assim:
- Transição: de novo, colocarei várias coisas aqui, e agora só pegue o mínimo entre elas.
- Não colocamos nada em
:
.
- Colocamos o
-ésimo do grupo em
:
.
- Não colocamos nada em
- A resposta vai ser o maior
tal que existam quaisuer
e
com
.
Temos estados, cada transição é
, logo a complexidade final é
.
OBS: A complexidade acaba ficando apertada, então para ficar tranquilo você tem que fazer pequenas "otimizações" (basicamente não passar por estados impossíveis), veja o código abaixo para ver as que fiz.