背包问题小结

一、背景

1.1 背包问题分类

在这里插入图片描述
背包问题可以分为8种类型,其中最基础的是0/1背包问题。作为动态规划的典型问题,其状态转移方程往往需要认真理解并能自行推出。这八种问题分别为:0/1背包问题、完全背包问题、多重背包问题、混合三种背包问题、二维费用背包问题、分组背包问题、有依赖的背包问题、求背包问题的方案总数。

二、0/1背包问题

2.1 问题描述:有N件物品和一个容量为V的背包。第i件物品的费用(即体积,下同)是w[i],价值是val[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

2.2解题思路
用动态规划的思路,阶段就是“物品的件数”,状态就是“背包剩下的容量”,那么很显然 f [ i ][ j ] 就设为从前 i 件物品中选择若干件放入容量为 v 的背包最大的价值
那么二维状态转移方程为:

f[i][j]=max(  f[i-1][j],f[i-1][j-W[i]]+V[i]  );

2.3 空间优化
首先,对于二维数组的背包来说,正序和逆序是无所谓的,因为你把状态都保存了下来,而一维数组的背包是会覆盖之前的状态的

要想知道f[ i ][ j ]你要从f [ i-1 ][ j ]和f [ i-1][ j-w[i] ]+va[i] 两个状态转移而来,这两个状态可以直接从二维数组中取出
一维数组的转移方程:

f[j]=max( f[j],f[j-W[i]]+V[i] );

f[j]表示在执行i次循环后(此时已经处理i 个物品),前i 个物体放到容量j的背包时的最大价值,即之前的f[i][j] 。与二维相比较,它把第一维压去了,但是二者表达的含义是相同的,只不过f[j] 一直在重复使用,所以,也会出现第i 次循环覆盖第i−1 次循环的结果。

memset(f,0,sizeof(f));
for(int i=1;i<=n;i++){
	cin>>V>>W;
	for(int j=C;j>=0;j--){//注意逆序
		if(j>=V)f[j]=max(f[j],f[j-V]+W);
	}
}

三、完全背包

3.1问题描述:有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是w[i],价值是val[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

3.2 解题思路:完全背包问题与0/1背包问题不同之处在于其每个物品是无限的,从每种物品的角度考虑,与它相关的策略就变成了取0件、1件、2件…。我们可以根据0/1背包的思路,对状态转移方程进行改进,令f[i][v]表示前 i 种物品恰放入一个容量为 v 的背包的最大权值。
状态转移方程就变成了:

f[i][j]=max(  f[i][j],f[i-1][j-k*V[i]]+k*W[i]   ); /* 0<= k*W[i] <=V */

化简过程(可能有些问题):

f[i][j-W[i]]=max( f[i][j-V[i]], f[i-1][j-W[i]-k*W[i]+k*V[i]  )
f[i][j-W[i]]+V[i]=f[i][j]

简化的二维状态转移方程:

f[i][j]=max( f[i][j] , f[i][j-W[i]]+V[i] );

一维度状态方程:

for i=1..N
    for v=0..V
        f[v]=max{f[v],f[v-c[i]]+w[i]}

注意这边是顺序的,而0-1背包是逆序的。
3.3 例题及AC代码
(题目链接 https://www.dotcpp.com/oj/problem1531.html)
3.3.1题目描述:一个正整数可以划分为多个正整数的和,比如n=3时:
3;1+2;1+1+1;共有三种划分方法。(数据规模和约定n< =100)
输入:一个正整数n
输出:一个正整数,表示划分方案数

#include<iostream>
#include<algorithm>
using namespace std;

int main(){
	int n;cin>>n;
	int bag[n+1][n+1]={0};
	for(int i=0;i<=n;i++)bag[1][i]=1;
	for(int i=2;i<=n;i++){
		for(int j=0;j<=n;j++){
			bag[i][j]=bag[i-1][j];
			if(j>=i)bag[i][j]+=bag[i][j-i];	
		}
	} 
	cout<<bag[n][n];	
	return 0;
} 

小结:

f[i][j]=max(  f[i-1][j], f[i-1][j-W[i]]+V[i]  );//0-1背包
f[i][j]=max(  f[i][j] ,  f[i][j-W[i]]+V[i] );	//完全背包
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值