Bribing Friends
Explicación
La dificultad de este problema viene del hecho de que podemos usar tanto moonies como cucuruchos de helado para sobornar amigos. Si solo tuviéramos un método de soborno, esto se convertiría en un simple problema de mochila 0/1.
Para simplificar, consideremos un gasto óptimo de cucuruchos de helado y moonies. Por cada cucurucho de helado que gastamos en una vaca dada, ahorramos moonie cada cucuruchos de helado. Intuitivamente, deberíamos distribuir los cucuruchos de helado entre los amigos con el más bajo, porque esto maximiza la cantidad de moonies que podemos ahorrar.
Esto se puede demostrar mediante un argumento de intercambio . Consideremos dos vacas y , donde . Si gastáramos cucuruchos de helado en la vaca en lugar de la vaca , entonces cada moonie que ahorráramos necesitaría cucuruchos de helado más.
Para pasar esto a un problema de mochila 0/1, sea la popularidad máxima posible alcanzable en el estado .
-
Para , representa la cantidad de cucuruchos de helado usados, mientras que se usaron moonies.
-
Para , representa la cantidad de moonies usados después de usar todos los cucuruchos de helado utilizables.
Nuestra transición de estado depende de la cantidad de moonies y cucuruchos de helado que nos quedan.
-
Si , ya agotamos todos los cucuruchos de helado, y solo podemos usar moonies para sobornar amigos.
-
Sea la cantidad restante de cucuruchos de helado en la posición
-
Si alcanza para sobornar por completo al amigo actual, podemos elegir sobornar a ese amigo y usar cucuruchos de helado en el proceso.
-
Si no alcanza para sobornar por completo al amigo actual, gastamos moonies para sobornar a ese amigo, empujando nuestro índice a ya que se consideran agotados todos los cucuruchos de helado.
-
Ordenamos los amigos por creciente. Esto garantiza que al recorrer a nuestros amigos, usamos nuestros helados primero en los amigos con menor , antes de usar algún moonie. Al final, tomamos el máximo de nuestro arreglo de DP como respuesta.
Implementación
Complejidad temporal:
#include <bits/stdc++.h>
using namespace std;
struct Friend {
int popularity, cost, discount;
Friend() : popularity(0), cost(0), discount(0) {}
};
bool x_sort(const Friend &f1, const Friend &f2) { return f1.discount < f2.discount; }
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, moonies, ice_cream;
cin >> n >> moonies >> ice_cream;
vector<Friend> friends(n);
for (int i = 0; i < n; i++) {
cin >> friends[i].popularity >> friends[i].cost >> friends[i].discount;
}
sort(friends.begin(), friends.end(), x_sort); // ordenamos por el mejor descuento
vector<int> dp(moonies + ice_cream + 1, -1); // -1 representa no visitado
dp[0] = 0;
for (const Friend &f : friends) {
auto [popularity, cost, discount] = f;
for (int i = moonies + ice_cream - cost; i >= 0; i--) {
// saltamos si no está visitado
if (dp[i] == -1) continue;
// gastando solo moonies
if (i >= ice_cream) {
dp[i + cost] = max(dp[i + cost], dp[i] + popularity);
}
// gastando solo cucuruchos de helado
else if (ice_cream - i >= cost * discount) {
dp[i + cost * discount] =
max(dp[i + cost * discount], dp[i] + popularity);
}
// gastando una cantidad mixta de moonies y cucuruchos de helado
else if (ice_cream + (cost - ((ice_cream - i) / discount)) <=
moonies + ice_cream) {
int moonies_spent = cost - ((ice_cream - i) / discount);
dp[ice_cream + moonies_spent] =
max(dp[ice_cream + moonies_spent], dp[i] + popularity);
}
}
}
cout << *max_element(dp.begin(), dp.end()) << '\n';
}