来几张乐色的笔记,供自己以后方便复习:
2. 01背包问题
有 N
件物品和一个容量是 V
的背包。每件物品只能使用一次。
第 i
件物品的体积是 vi,价值是 wi
。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,V
,用空格隔开,分别表示物品数量和背包容积。
接下来有 N
行,每行两个整数 vi,wi,用空格隔开,分别表示第 i
件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0 输入样例 输出样例: /** 第一个:二维数组进行状态设计: 第二个:滚动数组优化 第三个:与第二个一样,中间把状态转移方程合并在了一起; 3. 完全背包问题 有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。 第 i 种物品的体积是 vi,价值是 wi 。 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输入格式 第一行两个整数,N,V ,用空格隔开,分别表示物品种数和背包容积。 接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。 输出格式 输出一个整数,表示最大价值。 数据范围 0 输入样例 输出样例: /** // 二重循环的朴素方法: 4. 多重背包问题 I 有 N 种物品和一个容量是 V 的背包。 第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi 。 求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。 输入格式 第一行两个整数,N,V ,用空格隔开,分别表示物品种数和背包容积。 接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。 输出格式 输出一个整数,表示最大价值。 数据范围 0 输入样例 输出样例: * 由于每种物品有s[i]件可以选择,所以并不能优化成二重循环; * 把多重背包切成 0 1 背包问题; 5. 多重背包问题 II 有 N 种物品和一个容量是 V 的背包。 第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi 。 求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。 输入格式 第一行两个整数,N,V ,用空格隔开,分别表示物品种数和背包容积。 接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。 输出格式 输出一个整数,表示最大价值。 数据范围 0 提示: 本题考查多重背包的二进制优化方法。 输入样例 输出样例: * 状态设计:dp[i][j]:从前i种物品中选择不超过容量j的集合中价值最大的 9. 分组背包问题 有 N 组物品和一个容量是 V 的背包。 每组物品有若干个,同一组内的物品最多只能选一个。 ,价值是 wij,其中 i 是组号,j 是组内编号。 求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。 输出最大价值。 输入格式 第一行有两个整数 N,V ,用空格隔开,分别表示物品组数和背包容量。 接下来有 N 组数据: ,表示第 i 输出格式 输出一个整数,表示最大价值。 数据范围 0 输入样例 输出样例: /** // 二维数组进行状态设计 /** // 一维数组(滚动数组)进行状态设计
08
* 状态设计:dp[i][j] : 表示不超过前i件物品,且容量不超过j 的最大价值
* 方案;
* 状态转移方程:dp[i][j] = max(dp[i-1][j] , dp[i-1][j-w[i]] +c[i]);
*
* 由于每一次更新dp[i][j]的值都只用到了第一维的值,所以我们可以只要一个
* 一位数组来存储,这个一维数组称为滚动数组;
* 但是当j作为背包的容量时,j从小到大或者从大到小的选择不当时,状态
* 转移方程就会发生错误;
* 当需要用到滚动数组的时候,先求出状态转移方程的朴素方程,再将用滚动
* 数组的状态转移方程转换朴素法,如果与朴素方法相同,则此用滚动数组优
* 化的状态转移方程是为正确;
*
* 题目中的状态转移方程瞬息万变,应该掌握这种方法,理解这种思想,而
* 不能去死记硬背;
*/
输出最大价值。
010
* 完全背包问题,每次考虑第i种物品的时候,能够选择若干件,所以在最开始
* 求dp[i][j] 的最大值的时候,我们要把选择0,1,……,k件第i种物品的情况
* 都要进行比较;
* 这就是最初始的朴素代码:
* for(int i=1;i<=n;++i)
for(int j=0;j<=v;++j)
for(int k=0;k*w[i]<=j;++k)
dp[i][j] = max(dp[i][j],dp[i-1][j-k*w[i]]+k*c[i]);
//注意状态转移方程并不是:
// dp[i][j] = max(dp[i-1][j],dp[i-1][j-k*w[i]]+k*c[i]);
//因为 dp[i][j] = max{ dp[i-1][j-k*w[i]] + k*c[i] }(k=0,1,……,k);
//比较 dp[i][j] 的最大值,那么max 的一个参数肯定是dp[i][j];
// 但是又得和二重循环的状态转移方程区别开来看;
*/
// 三重循环的朴素方法:
/**
* 因为 dp[i][j] = max(dp[i-1][j] , dp[i-1][j-w[i]]+c[i] ,
* dp[i-1][j-w[i]*2]]+2*c[i],……,)
*
* 又因为 dp[i][j-w[i]]=max(dp[i-1][j-w[i]] , dp[i-1][j-2*w[i]]+c[i]],
* dp[i-1][j-3*w[i]]+2*c[i], ……,);
* 所以 dp[i][j] = max(dp[i-1][j],dp[i][j-w[i]]);
*/
// 滚动数组优化;
// dp[j] = max(dp[j],dp[j-w[i]]+c[i]);
输出最大价值。
010
* 这就和完全背包的三重循环朴素做法是差不多的思想,只是在第三个for循环
* 那儿判断k是否没有超过s[i];
*
for(int j=1;j<=s;++j)
for(int k=v;k>=w;--k)
dp[k]=max(dp[k],dp[k-w]+c);
把s件相同的物体看成01背包中s种不同的物体,
目的就是为了降低空间复杂度,时间复杂度是差不多的;
输出最大价值。
010
* 方案;
* 状态计算:dp[i][j] = { dp[i-1][j-k*w[i]] + k*c[i] };
*
* 每种物品有s件,每种物品选择k件,难道k真的需要从 0 枚举到 s 吗?
* 其实并不需要,我们可以用二进制来优化;
* 因为32位的二进制位可以表示的数是(0 —— 2^32 -1),那么我们用合适的
* 二进制组合一定能表示(0——s)的所有数;
*
* 假设s用二进制表示的组合是:1,2,4,8,...,2^k,t;(t>=0 && t<2(k+1))
* (即 s = 1+2+4+8+...+2^k+t ),其中2^k表示的是
* s用二进制表示的最后一个,也是最大的一个完整的二次方幂;
* 那么有 2^(k+1)-1 <= s && 2^(k+2)-1 > s 一定成立;
*
* 因此可得分解s的过程如下:
* int k=1;
while(k<=s)
{
++idx;
w[idx] = k*a;
c[idx] = k*b;
s-=k;
k*=2;
}
if(s > 0)
{
++idx;
w[idx] = s*a;
c[idx] = s*b;
}
因此分解完所有的物品以后,就可以看作是 01背包来解决;
每件物品的体积是 vij
08
* dp[i][j] = max(dp[i-1][j] , dp[i-1][j-w[i][k]]+c[i][k]);
*/
* dp[i][j] = max(dp[i-1][j] , dp[i-1][j-w[i][k]]+c[i][k]);
*/