需 n 經驗升級,忍耐度 m。k 種怪,每種無限量。殺第 i 種得 a_i 經驗,損 b_i 忍耐。最多殺 s 隻怪。求升級後剩餘最大忍耐度,不可行輸出 -1。
`dp[x][y]` = 損耗忍耐度 x、殺 y 隻怪所能獲得的最大經驗。
for each monster i:
for x = b_i .. m:
for y = 1 .. s:
for cnt = 1; cnt*b_i <= x && cnt <= y; ++cnt:
dp[x][y] = max(dp[x][y], dp[x-cnt*b_i][y-cnt] + cnt*a_i);
外層按忍耐度 x 從小到大枚舉,一旦 `dp[x][s] >= n` 即可輸出 `m - x`,保證剩餘忍耐度最大。
#include
#include
#include
using namespace std;
struct Monster { int exp, cost; } mon[105];
int dp[105][105];
int main() {
int n, m, k, s;
while (scanf("%d%d%d%d", &n, &m, &k, &s) == 4) {
for (int i = 1; i <= k; ++i) scanf("%d%d", &mon[i].exp, &mon[i].cost);
memset(dp, 0, sizeof(dp));
int ans = -1;
for (int x = 1; x <= m; ++x) { // 枚舉損耗忍耐度
for (int i = 1; i <= k; ++i) {
for (int y = 1; y <= s; ++y) {
for (int cnt = 1; cnt * mon[i].cost <= x && cnt <= y; ++cnt) {
dp[x][y] = max(dp[x][y], dp[x - cnt*mon[i].cost][y - cnt] + cnt*mon[i].exp);
}
}
}
if (dp[x][s] >= n) { ans = m - x; break; }
}
printf("%d\n", ans);
}
return 0;
}
for (int i = 1; i <= k; ++i) {
for (int x = mon[i].cost; x <= m; ++x) {
for (int y = 1; y <= s; ++y) {
dp[x][y] = max(dp[x][y], dp[x - mon[i].cost][y - 1] + mon[i].exp);
}
}
}
// 然後檢查最小 x 使 dp[x][s] >= n
注意:二維完全背包時,內層循環順序不影響正確性(因為兩維度都是正向),但必須保證每個物品的處理獨立。