[DP] 背包问题大总结

@kaike

1.01背包

有N件物品和一个容量为C的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使价值总和最大。

每种物品仅有一件,可以选择放还是不放。

用f[i][c]表示前i件物品恰好放入容量为c的背包里面,总价值最大。

f[i][c]=max{f[i-1][c],f[i-1][c-w[i]]+v[i]}

若不放第i件,就是前i-1件物品放入容量为c的背包里 f[i-1][c]

若放第i件,把前i-1件物品放入容量为 c-w[i] 的背包里,f[i-1][c-w[i]],总价值加上v[i]。

f[i][c]是由 f[i-1][c] 和 f[i-1][c-w[i]] 推出来的

如何在推f[i][c]的时候已知f[i-1][c] 和 f[i-1][c-w[i]]呢

f[i-1][c]就是不要这件物品,直接去取一个值

f[i-1][c-w[i]]就是要这件物品,不过你要取空出这个物品重量的最大价值量的值

由于刚开始时不能出现负值,第一次的时候c不能直接从0开始,需要一个缓冲。

于是二循环就要从背包容量开始倒推

注意看

f[i][c]的值只与f[i-1][c]和f[i-1][c-w[i]]有关,

如此倒推,完全可以转化为 f[c]=max{f[c],f[c-w[i]]+v[i]}

外面的f[c]是新值,里面的f[c]是上次循坏的值也就是f[i-1][c]

f[c-w[i]]同理也是上次循环的值

这样数组从二维到了一维,可以省空间。

时间复杂度(n*c)空间复杂度(c)

最大值当然要从背包满的时候f[c]处取得辣

1 for(int i=1;i<=n;i++)
2     for(int j=c;j>0;j--)
3         f[j]=max(f[j],f[j-w[i]]+v[i]);

细节问题

1.要求恰好装满背包,f[0]=0,f[1-v]=-∞

2.没要求恰好装满,f[0-v]=0

若要求恰好装满,则只要容量为0的背包恰好装满,而其他的都是未定义的状态

若不要求,所有值都有个确定值

2.完全背包问题

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

与01背包不同的是每件可以取无限件

问题十分棘手啊

f[i][c]=max{f[i-1][v-k*w[i]]+k*v[i]} 0<=k*w[i]<=v

若两件物品i、j满足c[i]<=c[j]且w[i]>=w[j],则将物品j去掉,不用考虑。

看起来小优化并没有优化掉什么复杂的东西,毕竟遇到一些极值还是要奔溃的

可以从01背包问题获得点启示

01背包二循环是从c-0开始的,因为他要先预留出来f[c-w[i]]的值

而完全背包就完全不用考虑这个问题,

我们从0-c开始循环

在考虑是否加这个物品的时候,可能正需要一个正好已选入第i个物品的值,所以就正着循环

1 for(int i=1;i<=n;i++)
2     for(int j=0;j<=c;j++)
3         f[j]=max(f[j],f[j-w[i]]+v[i]);
时间: 2024-10-24 04:42:33

[DP] 背包问题大总结的相关文章

hdu 4901 The Romantic Hero (dp+背包问题)

题意: 有n个数,从n个数中选出两个集合s和集合t,保证原序列中,集合s中的元素都在 集合t中元素的左边.且要求集合s中元素做抑或运算的值与集合t中元素做与运算的 值相等.问能选出多少种这样的集合s和t. 算法: 左右dp. 用dp[i][j]表示前i个数 做抑或运算得到j的方法数.最后一个值取不取到都不一定. 故为背包的问题.右边也是一样. 枚举时可能出现重复.枚举到第i个和枚举第i+1个可能重复.所以要枚举一个中间值. 这个中间值是归到s集的,因为抑或支持逆运算,而与是不支持的. 所以最后d

dp背包问题/01背包,完全背包,多重背包,/coin change算法求花硬币的种类数

一步一步循序渐进. Coin Change 具体思想:给你 N元,然后你有几种零钱S={S1,S2...,Sm} (每种零钱数量不限). 问:凑成N有多少种组合方式  即N=x1 * S1+x2*S2+...+xk*Sk (xk>=0,k=1,2..m) 设有f(x)中组合方式 有两种解答(自底向上回溯): 1.不用第m种货币   f(N,m-1) 2.用第m种货币 f(N-Sm,m) 总的组合方式为f(N,m)=f(N,m-1)+f(N-Sm,m) anything is nonsense,s

hdu 1203 dp背包问题

一个背包问题将价值变成了概率,求最小的一个收不到的概率,然后用1减去就可以啦! #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <set> #include <map> #include <string>

DP——背包问题(二)

01背包大家一定都会-- 但如果01背包的W很大怎么办-- 此时我们观察,若v[i]很小,我们可以考虑建立有关v[i]的方程 方程内容大概是:在达到某一v时,所需的总w最小 说多了不如上代码: 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cmath> 5 #include<string> 6 #include<algorithm> 7 u

DP——背包问题(一)

以前不是很重视 DP ,遇到 DP 就写贪心.暴搜--其实这是非常错误的,现在开始练习 DP 了才发现,我好菜-- 对于DP的整理,先从众所周知的背包问题开始. -------- 01背包:n 个物品,重量和价值分别为 w[i].v[i],背包容量 W,求所有挑选方案中价值总和的最大值. DP 方程 :dp[j] = max ( dp[j] , dp[ j - w[i] ] + v[i] ) ,其中 dp[j]为使用 j 的容量获得的最大价值,i 为第 i 件物品. 代码: 1 #include

HDU 1561 树形DP背包问题

这是自己第一道背包上树形结构问题,不是很理解这个概念的可以先看看背包九讲 自己第一次做,看了一下别人的思路,结合着对简单背包问题的求解方式自己一次AC了还是有点小激动的 题目大意是: 攻克m个城市,每个城市都有对应数量的宝贝,攻克某一个城市必须保证其对应的某一个特定城市已经被攻克,希望得到最多数量的宝贝 很容易根据题目画出一个对应关系的树形结构,每个节点都有一个对应的宝物的数量 我们用dp[i][j]存 i 号节点它下方子树中 有 j 个城市被攻克时得到的宝物最大数量 , 此时的 i 号是没有被

蒟蒻吃药计划-治疗系列 #round 3 背包问题大集合

序:部分背包 这一部分的内容是大家再熟悉不过的了 我举个栗子,金银岛 我想学过贪心的差不多都做过这道题 这一类问题有一个特点,就是物品可以分割(或者是可以选择物品的局部) 作为#round 3的序,我希望看过这篇博文的人能够认清这一类问题与后面的各类背包问题的区别 1.01背包 愉快的吃药就从现在开始啦! 按照套路,那个人应该出现了,不是么? :哇,居然被你猜对了 我想,我宁愿自己猜错了 :我今天又有一个问题,你能帮帮我吗? (内心:多少金币?) :不过这次没有金币. (我是拒绝的)什么问题?

HDU 5087 (线性DP+次大LIS)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5087 题目大意:求次大LIS的长度.注意两个长度相同的LIS大小比较,下标和大的LIS较大. 解题思路: 结构体记录当前点的最大长fir,次长sec. 对于f[i].fir的转移,其实就是裸的LIS. 只不过当f[j].fir+1>=f[i].fir的时候也要转移,这时候尽管两个LIS长度相等,但是大小不一样. 对于f[i].sec的转移,首先它的初始值是0,在a[i]>a[j]条件下: ①首先

hdu1203 dp背包问题

原来这就是背包呀,好吧没看题解我没写出,不过之前做过这种,就是求多项式的系数,这种模板还是很好用的,以后记住吧 //01背包模板 #include <iostream> #include <cstdio> #include <cmath> #include <cstdlib> #include <algorithm> #include <string> #include <stack> #include <queue