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<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<string>
 7 using namespace std;
 8 int n,W,w[2000],v[2000],dp[2000];
 9 int main(){
10     scanf("%d %d",&n,&W);
11     for(int i=1;i<=n;i++) scanf("%d %d",&w[i],&v[i]);
12     for(int i=1;i<=n;i++)
13     for(int j=W;j>=w[i];j--){
14         dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
15     }
16     printf("%d",dp[W]);
17     return 0;
18 }

01背包

———————— 完全背包:n 种物品,数目不限,其他的和 01背包 一 样。

DP 方程 : dp[j] = max ( dp[j] , dp[ j - w[i] + v[i] ) ,是不是感觉和 01背包 一样?其实,就是 一样……-- ^ --|||||,但代码有细微差别……

代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<string>
 7 using namespace std;
 8 int n,W,w[2000],v[2000],dp[2000];
 9 int main(){
10     scanf("%d %d",&n,&W);
11     for(int i=1;i<=n;i++) scanf("%d %d",&w[i],&v[i]);
12     for(int i=1;i<=n;i++)
13     for(int j=w[i];j<=W;j++){
14         dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
15     }
16     printf("%d",dp[W]);
17     return 0;
18 }

完全背包

那么,问题来了,为什么 01背包 是倒着
循环,而 完全背包 是正着循环……动笔模拟一下,你会发现且理解 — 正着循环会对一件物品重复选取╮(╯_╰)╭,而倒着循环就不会……

———————— 多重背包:n 种物品,给定数目 m[i],其他和 01 背包一样。

DP 方程:dp[j] =max ( dp[j],dp[ j - w[i] ] + v[i] ),&%&……¥怎么又一样,其实 多重背包 就是特殊处理化的 01背包,这个特殊处理就是 二进制拆分。

二进制拆分:众所周知什么是二进制,二进制拆分就是把 m[i] 个物品拆成 1个物品、2个物品组成的新物品、4个物品组成的新物品、8个物品组成的新物品……一                              直到不能拆为止,因为二进制可以 表示 出任何实数的嘛。

代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<string>
 7 using namespace std;
 8 int n,W,w[2000],v[2000],dp[2000],m[2000];
 9 int main(){
10     scanf("%d %d",&n,&W);
11     int x=n;
12     int y;
13     for(int i=1;i<=n;i++) scanf("%d %d %d",&w[i],&v[i],&m[i]);
14     for(int i=1;i<=x;i++){
15         y=1;
16         while(m[i]>y&&m[i]>1){
17             n++;
18             w[n]=y*w[i];
19             v[n]=y*v[i];
20             m[i]-=y;
21             y=y*2;
22         }
23         w[i]=m[i]*w[i];
24         v[i]=m[i]*v[i];
25     }
26     for(int i=1;i<=n;i++)
27     for(int j=W;j>=w[i];j--){
28         dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
29     }
30     printf("%d",dp[W]);
31     return 0;
32 }

多重背包

———————— 三种混合背包(01背包、完全背包、多重背包):这种问题就是给定 m[i],若 m[i] 为 -1,则为数目无限,否则数目有限,其他的和 01背包 一样。虽然写着是三种                              混合背包,但我认为实际上是两种背包,多重背包 和 完全背包。方法就是能拆分的就拆分,在循环的时候,判断一下,如果是 完全背包 就正着循环,不然就倒                                着循环…………蛮easy的QAQ~

代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<string>
 7 using namespace std;
 8 int n,W,w[2000],v[2000],dp[2000],m[2000];
 9 int main(){
10     scanf("%d %d",&n,&W);
11     int x=n;
12     int y;
13     for(int i=1;i<=n;i++) scanf("%d %d %d",&w[i],&v[i],&m[i]);
14     for(int i=1;i<=x;i++) if(m[i]!=-1) {
15         y=1;
16         while(m[i]>y&&m[i]>1){
17             n++;
18             w[n]=y*w[i];
19             v[n]=y*v[i];
20             m[i]-=y;
21             y=y*2;
22         }
23         w[i]=m[i]*w[i];
24         v[i]=m[i]*v[i];
25     }
26     for(int i=1;i<=n;i++)
27         if(m[i]!=-1)
28         for(int j=W;j>=w[i];j--){
29             dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
30     }
31     else
32         for(int j=w[i];j<=W;j++)
33              dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
34     printf("%d",dp[W]);
35     return 0;
36 }

混合背包

时间: 2024-10-12 04:41:19

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>

HDU 1561 树形DP背包问题

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

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] 背包问题大总结

@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-

hdu1203 dp背包问题

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

URAL 1108 简单的树形dp背包问题

题目大意: 一颗苹果树上,每条边都对应了一个权值,最后留下包括root : 1在的含有 m 条边的子树 , 希望留下的子树中权值之和最大 这里保留m条边,我们可以看作是保留了 m + 1 个点 令dp[u][j] 表示 u 为根的子树中包含了j个点的子树中得到的权值最大和 状态转移方程: dp[u][j] = max{dp[v][k] + dp[u][j-k] + e[i].d} v为u的子节点 j>k>=1 ,  1<=j<=m+1 1 #include <cstdio&g

POJ-1837 Balance (DP背包问题)

Balance Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 13395   Accepted: 8382 Description Gigel has a strange "balance" and he wants to poise it. Actually, the device is different from any other ordinary balance. It orders two arms