需要達到經驗值 n,初始忍耐度 m。有 k 種怪物,第 i 種給經驗 a[i]、消耗忍耐 b[i]。最多打 s 隻怪。求剩餘忍耐度最大值(即總消耗最小),無法達標輸出 -1。
b[i],容量 msa[i]dp[j][l]:消耗忍耐 j、打怪 l 隻時的最大經驗值。
轉移方程(完全背包正序,因同種怪可重複打):
for i = 0..k-1:
for j = b[i]..m: // 忍耐度正序
for l = 1..s: // 打怪數正序
dp[j][l] = max(dp[j][l], dp[j-b[i]][l-1] + a[i])
#include <stdio.h>
#include <string.h>
int main() {
int n, m, k, s;
int a[102], b[102];
int dp[102][102]; // m, s ≤ 100
while (scanf("%d %d %d %d", &n, &m, &k, &s) != EOF) {
for (int i = 0; i < k; i++)
scanf("%d %d", &a[i], &b[i]);
memset(dp, 0, sizeof(dp));
// 完全背包:三重循環,正序
for (int i = 0; i < k; i++) {
for (int j = b[i]; j <= m; j++) {
for (int l = 1; l <= s; l++) {
dp[j][l] = max(dp[j][l], dp[j - b[i]][l - 1] + a[i]);
}
}
}
// 尋找答案:經驗 ≥ n 且忍耐消耗最小(剩餘最大)
int ans = -1;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= s; j++) {
if (dp[i][j] >= n) {
ans = m - i; // 剩餘忍耐
break;
}
}
if (ans != -1) break;
}
printf("%d\n", ans);
}
return 0;
}
// 01 背包:逆序
for i: for v=V..cost1: for u=U..cost2: dp[v][u]=max(dp[v][u], dp[v-c1][u-c2]+w)
// 完全背包:正序
for i: for v=cost1..V: for u=cost2..U: dp[v][u]=max(dp[v][u], dp[v-c1][u-c2]+w)
// 多重背包:二進制拆分轉 01,或用 used 數組法