Solução por Lúcio Figueiredo
Conhecimento prévio necessário:
Uma das grandes dificuldades presentes nesse problema é o valor alto das constantes , e . Isso nos impede de usar, por exemplo, algum tipo de programação dinâmica que dependa de tais constantes nos seus estados.
No entanto, há dois valores "pequenos" no problema: (que é no máximo ) e o somatório de pássaros nas árvores (no máximo ). Vamos explorar este fato, pensando em algum tipo de programação dinâmica que use estas constantes pequenas em seus estados.
Vamos então definir a seguinte DP: é a quantidade máxima de magia que podemos obter no momento em que saímos da árvore e vamos para a árvore , assumindo que já conseguimos chamar pássaros.
Note que, caso seja possível calcular a DP de maneira eficiente, a resposta do problema é o máximo valor tal que , já que só podemos chamar uma quantidade de pássaros se em todos os momentos estivermos com uma quantidade positiva de pontos de magia. Portanto, o que resta é achar uma maneira eficiente de calcular a DP.
Imagine que acabamos de chegar na árvore , já chamamos pássaros e vamos chamar dentre os pássaros na árvore (). Ignorando o fato de que há uma capacidade máxima de magia, podemos obter a seguinte recorrência:
.
Isto se deve ao fato de que ao já contém a quantidade de magia que possuíamos logo após sair da árvore . Após escolhermos pássaros na árvore , perdemos de magia e ao prosseguirmos da árvore para a , ganhamos pontos de magia.
O único problema restante é a capacidade máxima de magia. Porém, podemos facilmente lidar com isso. Perceba que, se usarmos pássaros, a capacidade de magia que possuíremos é , pois possuíamos capacidade mágica inicialmente. Logo, se (o valor da recorrência acima) for maior que (a capacidade máxima de magia escolhendo pássaros), o valor de magia que obteremos é exatamente a capacidade máxima de magia. Caso contrário, o valor de magia será o valor da recorrência acima.
O que resta analisar é a complexidade da solução. Como a DP possui estados de complexidade e , (onde é o maior valor ) e há transições na -ésima árvore (precisamos escolher um entre e ), em uma primeira análise podemos afirmar que a complexidade será . Porém, na verdade, a complexidade da DP será , e esté valor é, nó máximo, . Portanto, a solução é eficiente.
Confira o código abaixo para melhor entendimento:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Problemas da Semana Noic - Avançado Semana 2 | |
// Pássaros - Complexidade O(c * sum(c_i)) | |
#include <bits/stdc++.h> | |
using namespace std; | |
typedef long long ll; | |
const int maxn = 1e3+10; | |
const int maxc = 2e4+10; | |
const ll inf = 1e18; // valor "infinito" (muito grande) | |
ll dp[maxn][maxc]; | |
int c[maxn]; | |
int cost[maxn]; | |
int main(void) | |
{ | |
int n, W, B, X; | |
scanf("%d %d %d %d", &n, &W, &B, &X); | |
for (int i = 1; i <= n; i++) | |
scanf("%d", &c[i]); | |
for (int i = 1; i <= n; i++) | |
scanf("%d", &cost[i]); | |
// inicializamos dp com valores muito pequenos | |
for (int i = 0; i <= n; i++) | |
for (int j = 0; j < maxc; j++) | |
dp[i][j] = -inf; | |
// caso base (antes de chegar na árvore 1, com 0 pássaros) | |
dp[0][0] = W; | |
for (int i = 1; i <= n; i++) | |
{ | |
for (int j = 0; j <= 10000; j++) | |
{ | |
for (int k = 0; k <= c[i]; k++) | |
{ | |
// só podemos usar k pássaros se não ultrapassarmos a quantidade de magia atual | |
if (dp[i-1][j] - 1ll*k*cost[i] >= 0) | |
{ | |
ll cap = 1ll*B*(j+k) + 1ll*W; | |
dp[i][j+k] = max(dp[i][j+k], min(cap, dp[i-1][j] + 1ll*X - 1ll*cost[i]*k)); | |
} | |
} | |
} | |
} | |
// encontramos a maior quantidade possível de pássaros | |
for (int j = 10000; j >= 0; j--) | |
{ | |
if (dp[n][j] >= 0) | |
{ | |
printf("%d\n", j); | |
return 0; | |
} | |
} | |
} |