【DP_背包专辑】【from zeroclock's blog】
本文转自:zeroclock的博客
这短时间看了论文《背包九讲》,看到背包问题解法中的优美之处也看到背包问题在现实中的应用,总结出一句话:背包问题值得一看。
背包问题可以概括为这样的模型:有若干种选择,每种选择有一定的代价和价值,做某些选择会得到特定的状态,问我们在约定的条件下怎么得到特定的状态?这里的状态可以是代价和或者价值和或者由其他这两者组合而来的状态。这类问题需要枚举每种状态,但是可以通过动态规划减少枚举的次数,提高效率,主要思想是每次都利用前面得到的状态进行转移得到当前的状态。这类问题很少能用贪心的,首先,贪心很难证明策略是否正确,其次贪心必定使得枚举量大量减少,会导致结果错误。
个人认为背包九讲中的很多模型本质都是两种模型:01背包模型、完全背包模型。大家可以把这两种模型理解透彻,然后再看其他模型,这样必定事半功倍。
看《背包九讲》的过程中开了一个专题,大概26道题目,主要是Hdu和Poj的题目,题目有难有易,这次的专辑主要讲解这些题目,还有一些Uva的简单背包问题也会加入到这个专辑中。本专辑都只列举基本思路,如果大家想看详细思路或者代码请去我博客中查找,大部分题目我都有写解题报告。
一、01背包问题 (先枚举物品,再逆序枚举容量)
1 //二维0/1背包 2 #include<stdio.h> 3 #include <algorithm> 4 #include<string.h> 5 using namespace std; 6 const int MAXV=1005; 7 const int MAXN=1005; 8 int dp[MAXN][MAXV]; 9 int w[MAXN]; 10 int c[MAXN]; 11 12 int main(){ 13 int T,n,v; 14 scanf("%d",&T); 15 while(T--){ 16 scanf("%d%d",&n,&v); 17 for(int i=1;i<=n;i++) 18 scanf("%d",&w[i]); 19 for(int i=1;i<=n;i++) 20 scanf("%d",&c[i]); 21 memset(dp,0,sizeof(dp)); //初始化 22 for(int i=1;i<=n;i++) 23 for(int j=0;j<=v;j++){//注意要从0开始,这个题测试数据有点变态,有的骨头有价值,但占的空间是0 24 dp[i][j]=dp[i-1][j]; 25 if (j>=c[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i]); 26 /*if (j>=c[i] && dp[i-1][j]<dp[i-1][j-c[i]]+w[i]) //比上一种写法 效率稍高 27 dp[i][j]=dp[i-1][j-c[i]]+w[i]; 28 else dp[i][j]=dp[i-1][j]; //不放i 或者i放不下 29 */ 30 } 31 printf("%d\n",dp[n][v]); 32 33 } 34 return 0; 35 }
1 //一位维0/1背包 2 #include<stdio.h> 3 #include <algorithm> 4 #include<string.h> 5 using namespace std; 6 const int MAXV=1005; 7 const int MAXN=1005; 8 int dp[MAXN]; 9 int w[MAXN]; 10 int c[MAXN]; 11 12 int main(){ 13 int T,n,v; 14 scanf("%d",&T); 15 while(T--){ 16 scanf("%d%d",&n,&v); 17 for(int i=1;i<=n;i++) 18 scanf("%d",&w[i]); 19 for(int i=1;i<=n;i++) 20 scanf("%d",&c[i]); 21 memset(dp,0,sizeof(dp)); //初始化 22 for(int i=1;i<=n;i++) 23 for(int j=v;j>=c[i];j--){ 24 dp[j]=max(dp[j],dp[j-c[i]]+w[i]); 25 } 26 printf("%d\n",dp[v]); 27 28 } 29 return 0; 30 }
2、Poj 3624 Charm Bracelet 赤裸裸的01背包问题。解题报告
3、Hdu 2546 饭卡 n种菜选若干种使剩下的钱最少,背包容量是开始时的钱,物品体积是菜的价格,状态转移时记录答案。解题报告
4、Uva 624 CD 常规的01背包,但要输出路径,状态转移时记录当前状态下当前物品是否被选用,然后递归求解就好。解题报告
5、Uva 562 Dividing coins 平衡问题,将n个硬币的总价值累加得到sum,再用sum/2作为背包容量对n个硬币做01背包处理,看能组成的最大容量是多少。解题报告
6、Hdu 2955 Robberies (推荐)抢劫方案最优问题,需要一个简单地转换,我们求的是不被抓的概率而非被抓的概率,各个银行的储蓄总和为背包容量,体积为单个银行 的储蓄,价值为不被抓概率。解题报告
7、Poj 2184 Cow Exhibition(推荐)变形的01背包,其实问题的本质是保证智商和幽默感和不为负数情况下的最大和。智商属性体积,幽默感属性为价值,问题转换为求体积大等于0时的体积、价值总和。 解题报告
8、Hdu 2639 Bone Collector II 求价值第K大的01背包问题,技巧是多加一维表示第k大时的价值,转移的时候用两个有序数列合并的方法不断更新第二维。解题报告
9、Poj 2923 Relocation(推荐,较综合) 用到状态压缩思想的01背包。先枚举选若干个时的状态,总状态量为1<<n,判断集合里的物品能否一次运走,如果能运走,那就把这个状态看成一个物品。预处理完找到tot个物品,再对这tot个物品进行01背包处理,每个物品的体积是state[i],价值是1,求必选n个物品的最少价值。状态转移方程:dp[j|k] = min(dp[j|k],dp[k]+1) (k为state[i,1<=j<=(1<<n)-1])
二、完全背包问题(先枚举物品,再正序枚举容量)
五、树形背包问题(在树上进行分组背包处理)
1、Poj 1155 TELE 把每个节点的子节点看成一组背包,最大容量是这点的叶子子孙数量,选几个节点就是选择的容量,价值就是用户给的Money-中转费用。解题报告Here9、Zoj 3627 Treasure Hunt II 树形DP +分组背包,浙大月赛的水题,很普通的树形背包。