ACM背包算法KnapsackAlgorithm

本文介绍了背包问题的基本模型,包括01背包(每件物品仅能取一次)、完全背包(物品可无限取)和多重背包(物品数量有限)。文章详细阐述了解决方法,如状态转移方程和优化策略,以及二维费用背包的处理方式。
摘要由CSDN通过智能技术生成

基本模型

有一个容量为V的背包和若干种物品,在一定限制条件下(物品占用容量),最多能放进多少价值的物品。(本质是组合问题)

背包的每个容量就是DP中的“状态”。

01背包

基本模型

N件物品和一个容量为V的背包。第i件物品的费用(占用体积)是c[i],价值是w[i],求使价值总和最大的方式。(每件物品只有一个

解决方法

无序变有序

        依次考虑前1、前2...、前i个物品。

定义状态

        f[i][j]表示前i件物品放入容量为j的背包的最大价值。(要枚举每个状态的值)

状态转移方程

        f[i][j] = max(f[i-1][j], f[i-1][j-w[i]] + w[i])

经典例题

骨头收集者

        解:横坐标为容量状态,纵坐标为不同的物品(骨头)的重量和价值。枚举每一个状态。所要求的只有最右下角的那个数据。

         优化:只使用一维数组记忆数据,但是每次循环要从后往前进行(因为是01背包,每件物品只能放一次,同时下一次操作不能影响上一次的结果),而且只需要进行到价值为w[i]的地方即可(因为小于w[i]的状态当前物品肯定放不下,只需要保留之前的运算结果就行了)。

#include <cstdio>
#include <cstring>

int val[1002] = {0};    // value
int vol[1002] = {0};    // volume
int dp[1002] = {0};
int max(int x, int y)
{
	return x >= y ? x : y;
}

int main()
{
	int testNum = 0, N = 0, V = 0;
	scanf("%d", &testNum);
	while(testNum--)
	{
		memset(dp, 0, sizeof(dp));
		scanf("%d%d", &N, &V);
		for(int i = 1; i <= N; i++)
			scanf("%d", &val[i]);
		for(int i = 1; i <= N; i++)
			scanf("%d", &vol[i]);
		for(int i = 1; i <= N; i++)
			for(int j = V; j >= vol[i]; j--)
				dp[j] = max(dp[j], dp[j-vol[i]]+val[i]);
		printf("%d\n", dp[V]);
	}
	return 0;
}

完全背包

基本模型

一种物品可以取无数个。

解决方法

*(1)暴力转化为01背包,把一件物品当成多件来处理。

(2)把01背包中的记忆数组调整为从前往后滚动(因为一件物品可以取无数个,前面假如放过该物品,当容量增加时还可以继续放)。

拓展--背包必须装满

        :将一维数组除第一项(初始化为0)以外的每一项都初始化为-1,用-1来表明没有装满(不合格的情况)。在之后的判断中,只要该容量的子情况中包含没有装满的情况,那这种容量就不合格(不可能装满),设置为-1。最后还是看最右下角的值是正常值还是-1。

多重背包

基本模型

一种物品的数量既不是1个也不是无数个。

解决方法

二进制优化

        将物品进行拆分,转换为01背包。

        eg. 对于13个相同的物品可以拆分成4组(1、2、4、6),这四组可以组成任意一个1~13之间的数字。

        因此可以将一种有C个的物品拆分成:1、2、4、8...、2^(k-1)、C-2^k+1。

        拆分参考代码

int t=1, cnt=1;
while(x >= t)
{
    v[cnt] = a * t;
    c[cnt++] = b * t;
    x -= t;
    t <<= 1;
}
if(x)
{
    v[cnt] = a * x;
    c[cnt++] = b * x;
}

二维费用背包

基本模型

每件物品都具有两种不同的费用。

解决方法

增加一个维度即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值