01背包

动态规划是一种高效的算法。在数学和计算机科学中,是一种将复杂问题的分成多个简单的小问题思想 ---- 分而治之。因此我们使用动态规划的时候,原问题必须是重叠的子问题。运用动态规划设计的算法比一般朴素算法高效很多,因为动态规划不会重复计算已经计算过的子问题。因为动态规划又可以称为“记忆化搜索”。

01背包是介绍动态规划最经典的例子,同时也是最简单的一个。我们先看看01背包的是什么?

问题(01背包):
有n个重量和价值分别为vi和ci的物品。从这些物品中挑出总重量不超过m的物品,求所有挑选方案中价值总和的最大值。

这就是被称为01背包的问题。在没学习动态规划之前,我们看到这个问题第一反应会用dfs搜索一遍。那我们先使用这种方法来求解01背包问题:

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e3+;
int n,m,v[N],c[N];
//从第i个物品开始挑选总重量不大于sumv的部分
int dfs(int now,int sumv){
int res;
if(now==n+) return res=;//已经没有剩余物品
if(sumv<v[now]) res=dfs(now+,sumv);//无法挑选第i个物品
else res=max(dfs(now+,sumv),dfs(now+,sumv-v[now])+c[now]);//比较挑和不挑的情况,选取最大的情况
return res;
}
int main(){
scanf("%d%d",&m,&n);
for(int i=;i<=n;i++) scanf("%d%d",&v[i],&c[i]);
printf("%d",dfs(,m));
return ;
}
/*
期望得分:30分
*/

乍一看dfs好像就可以解决这个问题,那还有动态规划什么事。然而我们仔细分析一下时间复杂度,每一种状态都用选或者不选两种可能。所以我们可以得出使用dfs的时间复杂度为O(2^n)。显然这个方法不是一个很好的方法,因为这个时间复杂度太高了。我们仔细研究可以发现,造成时间复杂度这么高的原因是重复计算。既然我们找到复杂度这么高的原因,那我们就可以想办法减少它重复计算的次数。仔细分析容易想到,使用一个二维数组来记录每一次搜索的答案,这样我们就避免了重复计算。

这样的小技巧,我们称之为记忆化搜索。我们只是小小的改变就让它的时间复杂度降低至O(nW)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e3+;
int n,m,v[N],c[N],dp[N][N*];//保存每一次搜索的答案
int dfs(int now,int sumv){
if(dp[now][sumv]!=-) return dp[now][sumv];
int res;
if(now==n+) return dp[now][sumv]=;
if(sumv<v[now]) res=dfs(now+,sumv);
else res=max(dfs(now+,sumv),dfs(now+,sumv-v[now])+c[now]);
return dp[now][sumv]=res;
}
int main(){
memset(dp,-,sizeof dp);//初始化dp数组的值,使其全为-1
scanf("%d%d",&m,&n);
for(int i=;i<=n;i++) scanf("%d%d",&v[i],&c[i]);
printf("%d",dfs(,m));
return ;
}
/*
期望得分:60-80分
*/

  仔细分析,可以发现我们还可以有更简单的写法(转成递推):

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e3+;
int n,m,v[N],c[N],dp[N][N*];
//dp[i][j] 表示取了i个物品挑选出总重量不超过j的物品时,背包中的最大价值
int main(){
memset(dp,-,sizeof dp);
scanf("%d%d",&m,&n);
for(int i=;i<=n;i++) scanf("%d%d",&v[i],&c[i]);
for(int i=;i<=m+;i++) dp[n+][i]=;//还没开始挑选的时候,背包里的总价值为0
for(int i=n;i;i--){
for(int j=m;j>=;j--){
if(j<v[i]) dp[i][j]=dp[i+][j];
else dp[i][j]=max(dp[i+][j],dp[i+][j-v[i]]+c[i]);
}
}
printf("%d",dp[][m]);
return ;
}
/*
期望得分:60-80分
*/

  然后dp[i][j]的仅由dp[i+1][j]||dp[i+1][j-v[i]]转移而来,于是我们可以滚动第一维:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e3+;
int n,m,v[N],c[N],dp[][N*];
int main(){
memset(dp,-,sizeof dp);
scanf("%d%d",&m,&n);
for(int i=;i<=n;i++) scanf("%d%d",&v[i],&c[i]);
int now=;//开启滚动
for(int i=;i<=m;i++) dp[now][i]=;//当前状态初始化
for(int i=n;i;i--){
now^=;
for(int j=m;j>=;j--){
if(j<v[i]) dp[now][j]=dp[now^][j];
else dp[now][j]=max(dp[now^][j],dp[now^][j-v[i]]+c[i]);
}
}
printf("%d",dp[now][m]);
return ;
}
/*
期望得分:100分
*/

  凡是可以滚动的,必定可以降维。——shenben

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e3+;
int n,m,v[N],c[N],dp[N*];
int main(){
scanf("%d%d",&m,&n);
for(int i=;i<=n;i++) scanf("%d%d",&v[i],&c[i]);
dp[]=;
for(int i=;i<=n;i++){
for(int j=m;j>=v[i];j--){//v[i]以后的j对答案没有贡献
dp[j]=max(dp[j],dp[j-v[i]]+c[i]);
}
}
printf("%d",dp[m]);
return ;
}
/*
期望得分:100分
*/

使用递推方程直接求解的方法,我们称之为dp。因为他每一次的选取,都在动态的计算最优的情况。当然可能他局部不是最优,但是整体一定是最优解。这就是他和贪心算法最大的不同,贪心算法,每一次都是最优,但是整体不一定不是最优。

多重背包

 

问题(多重背包):
  就是一个0,,……k背包(往01背包上想就好了)
有n种重量和价值分别为vi和ci的物品,每种物品有si个。从这些物品中挑出总重量不超过m的物品,求所有挑选方案中价值总和的最大值

  搜索

//期望得分:30-70分
#include<cstdio>
#include<algorithm>
#define N 10100
using namespace std;
int v[N],c[N],s[N],n,m;
int ans;
void dfs(int now,int sumv,int sumc){
if(now==n+){if(sumv<=m) ans=max(ans,sumc);return ;}
dfs(now+,sumv,sumc);
for(int i=;i<=s[now];i++) dfs(now+,sumv+i*v[now],sumc+i*c[now]);
}
int main(){
scanf("%d%d",&m,&n);
for(int i=;i<=n;i++) scanf("%d%d%d",v+i,c+i,s+i);
dfs(,,);
printf("%d",ans);
return ;
}

  记忆化搜索

//期望得分:60-100分
#include<cstdio>
#include<algorithm>
#define N 10100
using namespace std;
int v[N],c[N],s[N],n,m;
int ans;
int dp[][N];
int dfs(int now,int sumv){
if(now==n+) return ;
if(dp[now+][sumv]) dp[now][sumv]=dp[now+][sumv];
else dp[now][sumv]=dfs(now+,sumv);
for(int i=;i<=s[now];i++){
if(sumv+i*v[now]>m) break;
if(dp[now+][sumv+i*v[now]]) dp[now][sumv]=max(dp[now][sumv],dp[now+][sumv+i*v[now]]+i*c[now]);
else dp[now][sumv]=max(dp[now][sumv],dfs(now+,sumv+i*v[now])+i*c[now]);
}
return dp[now][sumv];
}
int main(){
scanf("%d%d",&m,&n);
for(int i=;i<=n;i++) scanf("%d%d%d",v+i,c+i,s+i);
dfs(,);
printf("%d",dp[][]);
return ;
}

  记忆化搜索转递推

//期望得分:60-100分
#include<cstdio>
#include<algorithm>
#define N 10100
using namespace std;
int v[N],c[N],s[N],n,m;
int ans;
int dp[][N];
int main(){
scanf("%d%d",&m,&n);
for(int i=;i<=n;i++) scanf("%d%d%d",v+i,c+i,s+i);
for(int i=n;i;i--){
for(int j=m;j>=;j--){
for(int k=;k<=s[i];k++){
if(j-k*v[i]<) break;
dp[i][j]=max(dp[i][j],dp[i+][j-k*v[i]]+k*c[i]);
}
}
}
printf("%d",dp[][m]);
return ;
}

  滚动数组(滚动第一维)

//期望得分:100分
#include<cstdio>
#include<algorithm>
#define N 10100
using namespace std;
int v[N],c[N],s[N],n,m;
int ans;
int dp[][N];
int main(){
scanf("%d%d",&m,&n);
for(int i=;i<=n;i++) scanf("%d%d%d",v+i,c+i,s+i);
int now=;
for(int i=n;i;i--){
now^=;
for(int j=m;j>=;j--){
for(int k=;k<=s[i];k++){
if(j-k*v[i]<) break;
dp[now][j]=max(dp[now][j],dp[now^][j-k*v[i]]+k*c[i]);
}
}
}
printf("%d",dp[now][m]);
return ;
}

  降维(用二进制优化)

//期望得分:100分
#include<cstdio>
#include<iostream>
using namespace std;
inline int read(){
register int x=,f=;
register char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
const int N=1e5+;
int n,m,cnt,xp[];
int f[N*],v[N],c[N];
int main(){
for(int i=;i<=;i++) xp[i]=<<i;
m=read();n=read();
for(int i=,x,y,z;i<=n;i++){
x=read();y=read();z=read();
for(int t=;z>xp[t];t++){
v[++cnt]=x*xp[t];
c[cnt]=y*xp[t];
z-=xp[t];
}
if(z>){
v[++cnt]=x*z;
c[cnt]=y*z;
}
}
for(int i=;i<=cnt;i++){
for(int j=m;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+c[i]);
}
}
printf("%d",f[m]);
return ;
}

其他背包dp自行整理

背包dp整理的更多相关文章

  1. 背包DP 整理

    题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路 这是最基础的背包问题,特点是: ...

  2. hdu 5534 Partial Tree 背包DP

    Partial Tree Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid= ...

  3. HDU 5501 The Highest Mark 背包dp

    The Highest Mark Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?p ...

  4. Codeforces Codeforces Round #319 (Div. 2) B. Modulo Sum 背包dp

    B. Modulo Sum Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/577/problem/ ...

  5. noj [1479] How many (01背包||DP||DFS)

    http://ac.nbutoj.com/Problem/view.xhtml?id=1479 [1479] How many 时间限制: 1000 ms 内存限制: 65535 K 问题描述 The ...

  6. HDU 1011 树形背包(DP) Starship Troopers

    题目链接:  HDU 1011 树形背包(DP) Starship Troopers 题意:  地图中有一些房间, 每个房间有一定的bugs和得到brains的可能性值, 一个人带领m支军队从入口(房 ...

  7. BZOJ 1004: [HNOI2008]Cards( 置换群 + burnside引理 + 背包dp + 乘法逆元 )

    题意保证了是一个置换群. 根据burnside引理, 答案为Σc(f) / (M+1). c(f)表示置换f的不动点数, 而题目限制了颜色的数量, 所以还得满足题目, 用背包dp来计算.dp(x,i, ...

  8. G - Surf Gym - 100819S -逆向背包DP

    G - Surf Gym - 100819S 思路 :有点类似 逆向背包DP , 因为这些事件发生后是对后面的时间有影响. 所以,我们 进行逆向DP,具体 见代码实现. #include<bit ...

  9. 树形DP和状压DP和背包DP

    树形DP和状压DP和背包DP 树形\(DP\)和状压\(DP\)虽然在\(NOIp\)中考的不多,但是仍然是一个比较常用的算法,因此学好这两个\(DP\)也是很重要的.而背包\(DP\)虽然以前考的次 ...

随机推荐

  1. eclipse编码格式设置

    大家好,我是小Alan,很高兴大家能够看到这篇小小的技术点文章,这还是从参加工作以来,小Alan写的第一篇博文.喜欢能够给一些朋友带来方便. 说到eclipse编码格式的设置其实一个非常非常小的事情, ...

  2. 安装php扩展

    下面我以soap安装为例子 cd /home/zhangy/php-5.2.6/ext/soap /usr/local/php/bin/phpize#确定php-config文件在不在,调用php-c ...

  3. 关于激活Bentley软件详细步骤介绍(再补充一个)

    在安装完ContextCapture软件之后,大家怀着迫不及待的心情双击了运行快捷键.但是很遗憾的是,会产生下面的提示窗口: 也许大家并不在意,就觉得关掉这个窗口不就行了.然而,头疼的问题来了.这个窗 ...

  4. HTML5 respond.js 解决IE6~8的响应式布局问题

    HTML5 respond.js 解决IE6~8的响应式布局问题   响 应式布局,理想状态是,对PC/移动各种终端进行响应.媒体查询的支持程度是IE9+以及其他现代的浏览器,但是IE8在市场当中仍然 ...

  5. #winhec# 开发人员刷屏看点 (视频)

    今天大家已经被winhec刷屏了,本来不想写这篇了,但看了所有的文章,大家关注的都是windows 10的那些新功能,小米win10刷机,联想千元手机,小娜啥的.对于keynote上第二部分 Don ...

  6. H5一二事

    先回顾一下WEB技术的几个阶段 Web 1.0 内容为主,主要流行HTML和CSS Web 2.0 动态网页,流行AJAX/JavaScript/DOM H5 时代,WEB开发回归富客户端 那么H5肯 ...

  7. CVEH项目观察与思考

    2013-07-01 项目进展: 从启动至今已有三个星期,但是进展甚慢,取得的进展有: A. 封装成库,和HB调用库的接口有些进展,但进未完成 B. 整个框架,类视图,调用视图,只有两三层的进展: C ...

  8. IO流04--毕向东JAVA基础教程视频学习笔记

    Day20 01 File概述02 File对象功能-创建和删除03 File对象功能-判断04 File对象功能-获取05 File对象功能-文件列表106 File对象功能-文件列表207 列出目 ...

  9. Javascript之旅——第五站:说说那些所谓的包装类型

    最近不看犀牛书了,那本翻译的特烂而且好拗口,尤其是原型那块说的乱七八糟,后来经同事介绍,买了本js高级程序设计,然后就继续 苦逼的看,不吐槽了,继续说说js中有新鲜感的包装类型. 一:String 说 ...

  10. Windows自动关机命令

    winxp中自带了自动关机功能,在开始→运行中使用SHUTDOWN命令 1. 延迟关机关机 shutdown -s -t 120 -s为关机:-t为时间,以秒为单位,120表示2分钟 表示两分钟后关机 ...