FZUOJ 2214 Knapsack problem
之前的01背包都是在枚举背包的容量,时间复杂度可以优化到 O ( N ∗ V ) O(N*V) O(N∗V),但是如果背包的总的容量非常大的话,就没办法枚举背包容量来进行计算了。
题目描述(题目链接)
有一个背包,容量为 B B B,同时有 n n n个物品,每个物品有一个重量 w w w和价值 v v v,要求将物品装入背包中,使得总价值最大。
约束条件
- 1 ≤ n ≤ 500 1 \leq n \leq 500 1≤n≤500
- 1 ≤ B , w [ i ] ≤ 1000000000 1 \leq B,w[i] \leq 1000000000 1≤B,w[i]≤1000000000
- 1 ≤ v [ 1 ] + v [ 2 ] + v [ 3 ] + . . . + v [ n ] ≤ 5000 1 \leq v[1] + v[2] + v[3]+...+v[n] \leq 5000 1≤v[1]+v[2]+v[3]+...+v[n]≤5000
- 测试数据不超过 100 100 100个
- 所有输入 都是整数
输入
第一行表示测试数据的个数
对于每一组测试数据,首先输入 n n n和 B B B
接下来 n n n行,每行两个数表示 w [ i ] w[i] w[i]和 v [ i ] v[i] v[i]
输出
对于每组测试数据,输出一个相应的答案,并且换行
样例输入
1
5 15
12 4
2 2
1 1
4 10
1 2
样例输出
15
分析
这道题思路同 01 01 01背包相同,但是由于容量太大,不能像之前的方法来做,但是可以考虑更换 D P DP DP的对象。之前的 01 01 01背包中 d p [ i ] dp[i] dp[i]表示重量不超过 i i i的最大价值,这次可以换成 d p [ i ] dp[i] dp[i]表示价值不超过 i i i的最小重量
特别要注意的是由于表示最小重量,那么 d p dp dp数组应该初始化为一个很大的值,因为要不断更新最小重量,同时 d p [ 0 ] = 0 dp[0] = 0 dp[0]=0
找到最小重量后,就去从总的价值开始枚举,能够刚好满足背包的容量即可
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
const ll INF = 0x3f3f3f3f;
const int N = 1005;
const int MV = 10005;
ll w[N], v[N];
ll d[MV]; //d[i]表示价值为i的时候最小重量
int main () {
ll n, m, sum;
int T;
scanf("%d", &T);
while(T --) {
scanf("%lld %lld", &n, &m);
sum = 0; //记录一下总价值
for(ll i = 1; i <= n; i++) {
scanf("%lld %lld", &w[i], &v[i]);
sum += v[i];
}
for(int i = 1; i <= sum; i++) //数组初始化
d[i] = INF;
d[0] = 0;
for(int i = 1; i <= n; i++)
for(int j = sum; j >= v[i]; j--)
d[j] = min(d[j], d[j - v[i]] + w[i]);
for(ll i = sum; i >= 0; i--) {//找到第一个不大于m的对应的价值
if(d[i] <= m) {
printf("%lld\n", i);
break;
}
}
}
return 0;
}