Knapsack problem (超大01背包)

FZUOJ 2214 Knapsack problem

之前的01背包都是在枚举背包的容量,时间复杂度可以优化到 O ( N ∗ V ) O(N*V) O(NV),但是如果背包的总的容量非常大的话,就没办法枚举背包容量来进行计算了。

题目描述(题目链接)

有一个背包,容量为 B B B,同时有 n n n个物品,每个物品有一个重量 w w w和价值 v v v,要求将物品装入背包中,使得总价值最大。

约束条件

  • 1 ≤ n ≤ 500 1 \leq n \leq 500 1n500
  • 1 ≤ B , w [ i ] ≤ 1000000000 1 \leq B,w[i] \leq 1000000000 1B,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 1v[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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值