题意 :
以下两个问题的物品都只能取有且只有一次
① 给你 N 个物品,所有物品的价值总和不会超过 5000, 单个物品的价格就可达 10^10 ,背包容量为 B
② 给你 N (N ≤ 40 ) 个物品,物品的单个价值和重量都达到 10^15 问你在背包容量为 W
给出 ① 和 ② 问题条件下背包所能装出来的最大价值
分析 :
① 因为单个物品的价值实在太大,如果仍然按照普通 0/1 背包的 dp 定义方法数组是开不下的
但是发现价值的总和并不大,所以从价值这里下手,定义 dp[i][j] = 从 1 ~ i 个物品背包价值为 j 时的最小重量
dp 的状态转移方程效仿普通的 0/1 背包即可,但是是取最小 dp[i][j] = min(dp[i][j], dp[i-1][j-v[i]] + w[i])
同样可以使用 0/1 背包的空间优化将 dp 数组转化为一维的来减少空间复杂度
以下是 FZU 2214 的 AC 代码
#include<stdio.h> #include<string.h> #include<algorithm> #define LL long long using namespace std; const int maxn = 5e3 + 10; const LL INF = 0x3f3f3f3f; LL dp[maxn], w[555], B; int v[555]; int N, C; int main(void) { int nCase; scanf("%d", &nCase); while(nCase--){ scanf("%d %lld", &N, &B); C = 0; for(int i=1; i<=N; i++) scanf("%lld %lld", &w[i], &v[i]), C += v[i]; /// C 累加的是所有物品的价值总和 for(int j=0; j<=C; j++) dp[j] = INF;/// 初始化 dp 数组应当赋予一个较大的数 dp[0] = 0; for(int i=1; i<=N; i++) for(int j=C; j>=v[i]; j--) dp[j] = min(dp[j], dp[j-v[i]]+w[i]); for(int j=C; j>=0; j--){ if(dp[j] <= B){///从大到小遍历价值,第一个满足条件的 dp 数组值的下标便是答案 printf("%d\n", j); break; } } } return 0; }