这个01背包 , 理解了一天才勉强懂点 , 写个博客  (  推荐   http://blog.csdn.net/insistgogo/article/details/8579597)

题目 :

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

分析一下 :

  面对一件物品 , 我有两种选择 , 放或者不放 , 如果不放 , 则最大价值是 前 n - 1 件物品放入容量为 j 的背包 , 如果放 , 则最大价值是将前 n - 1 件物品放入剩余容量为 j - c[j] , 此时的

价值是 前 n - 1 件物品放入剩余容量为 v - c[ j ] 的价值加上放入该物品的价值 .......一直此过程 , 最后的最大价值即是 c[ n ] [ v ] 。

( 偷一个图 ):

  

  要怎样去实现这个代码呢 ? 先用最好的理解的二维数组去解决这个问题 , 两个维度分别存的是 物品的个数以及 当前背包的总容量 , 因为我第一层 for 是从 遍历所有的物品 , 即面对某个物品 , 第二层 for  我是遍历体积 , 每次让体积 + 1 , 这样就会产生一种 什么效果呢 ? 在面对每一个物品时 , 我所有的体积都去试一下 , 这个过程 从最终的意义上考虑实际都是在为最后 背包容量满的时候服务 , 这个过程 也就叫做构建最优子结构的过程 , 并且当前产生的结果 , 对后面不会有任何影响 , 也就是无后效性 。

  ( 关于这个代码 , 我感觉有一个地方很精髓 , 就是 当面对 一号 物品时 , 我的状态转移方程考虑的是前 i - 1 个物品 , 即没有物品的时候 , 此时我建立的数组就可以在 i = 0  的位置留出地方 , 并且借助 memset 将这些位置都给 0 )

  (还有 这个的背包容量也可以从 V 开始遍历 直到 1 结束 , 则会打出另一张表)

代码示例 :

  

// 感觉 DP 中的背包问题 就是一个制作表格的过程
// 所制作的表的大小是 n * v #include <iostream>
#include <algorithm>
using namespace std ; int dp[10][500] ; // 定义为全局变量 会被初始化为 0
int weight[15] ;
int value[15] ; int main ( ) {
int n , v ; cin >> n >> v ;
for ( int i = 1 ; i <= n ; i++ ) {
cin >> weight[i] >> value[i] ;
} for ( int i = 1 ; i <= n ; i++ ) {
for ( int j = 1 ; j <= v ; j++ ) {
if ( weight[i] <= j )
dp[i][j] = max ( dp[i-1][j] , dp[i-1][j-weight[i]] + value[i] ) ;
else
dp[i][j] = dp[i-1][j] ; // 如果当前面对此物品背包中不能再放东西 ,但表的这一栏又不能空 ,
// 所以填的数 是这个体积下,没有此物品的体积
}
} cout << dp[n][v] << endl ; return 0 ;
}

二 . 优化空间复杂度

  用二维数组去写的话 , 复杂度为 O (n*v) , 在数据大的时候直接超内存 , 所以可以借助一维数组 , 将复杂度优化为 O (n)

  贴上我的代码 :

    

// 感觉 DP 中的背包问题 就是一个制作表格的过程
// 所制作的表的大小是 n * v
#include <iostream>
#include <algorithm>
using namespace std ; int dp[1500] ; // 定义为全局变量 会被初始化为 0
int weight[15] ;
int value[15] ; int main ( ) {
int n , v ; cin >> n >> v ;
for ( int i = 1 ; i <= n ; i++ ) {
cin >> weight[i] >> value[i] ;
} for ( int i = 1 ; i <= n ; i++ ) {
for ( int j = v ; j >= 1 ; j-- ) {
if ( weight[i] <= j )
dp[j] = max ( dp[j] , dp[j-weight[i]] + value[i] ) ; // 当面对一个物品时 , 此时数组所对应的即是前 i - 1 的价值
}
} cout << dp[v] << endl ; return 0 ;
}

// 用一维数组做 , 感觉和数字三角形的题很像 , 从底下递推 。此背包问题就是 , 在面对每个物品时,不断地更新数组中的数据 , 并且在面对每个物品时 , 其体积是从 V 到 1 递推

// 用一维数组做 , 当面对一个物品 , 在这个位置上 ,数组所存的数即是 在该体积下 , 前 i - 1 件物品的最大价值

有一个问题 : 体积为什么要是逆序递推呢 ?

然后附上我程序的运行结果 :

  

三 .  初始化的细节问题 

  在最优解得背包问题 , 有两种问法 ;

 1 . 在不超过背包容量时 , 如何装会获得最大价值 ?

 2 .当背包恰好装满时 , 获得的最大价值是多少 ?

  两种问法的唯一区别就在于就在于背包是否装满 , 这两种问法的实现仅仅区别于对数组元素的初始化 。

  初始化 f 数组就表示 , 在没有放入任何物品时背包的合法状态 。

  对于恰好装满的情况 , 我应对此二维数组的 dp[ i ] [ 0 ]  初始化为 0 , 因为我在面对一个物品时 , 我此时背包的容量为 0 , 不能再放任何东西 , 即为恰好装满的时候 , 将此 dp 数组其余位置全部初始为  负无穷 , 其余过程全部同上 ... 最后时 输出恰好的最优解 即 dp[ n ][ v ] 。

  对于不超过背包容量的情况 , 我只需要将数组中的元素全部初始为 0 。如果背包并非 要全部装满 , 那么任何背包都有一个合法解 , 即什么也不装 。

恰好装满的二维数组代码 :

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std ; int dp[10][500] ; // 定义为全局变量 会被初始化为 0
int weight[15] ;
int value[15] ; int main ( ) {
int n , v ;
int i , j; cin >> n >> v ;
memset ( dp , 0x8f , sizeof(dp) ) ; // 将数组全部元素初始化为 负无穷
for ( i = 0 ; i <= n ; i++ ) {
dp[i][0] = 0 ;
} for ( i = 1 ; i <= n ; i++ ) {
cin >> weight[i] >> value[i] ;
} for ( i = 1 ; i <= n ; i++ ) {
for ( j = 1 ; j <= v ; j++ ) {
if ( weight[i] <= j ) {
dp[i][j] = max ( dp[i-1][j] , dp[i-1][j-weight[i]] + value[i] ) ;
}
else
dp[i][j] = dp[i-1][j] ;
}
} cout << dp[n][v] << endl ;
return 0 ;
}

恰好装满的一维数组的代码 :

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std ; int dp[1500] ; // 定义为全局变量 会被初始化为 0
int weight[15] ;
int value[15] ; int main ( ) {
int n , v ; memset ( dp , 0x8f , sizeof(dp) ) ;
dp[0] = 0 ;
cin >> n >> v ;
for ( int i = 1 ; i <= n ; i++ ) {
cin >> weight[i] >> value[i] ;
} for ( int i = 1 ; i <= n ; i++ ) {
for ( int j = v ; j >= weight[i] ; j-- ) {
dp[j] = max ( dp[j] , dp[j-weight[i]] + value[i] ) ; // 当面对一个物品时 , 此时数组所对应的即是前 i - 1 的价值
}
} cout << dp[v] << endl ; return 0 ;
}

附上利用机器打得表 :

  

四 . 一个常数的优化

  在用一维数组 , 做背包问题时 , 背包容量是从  v 到 1 遍历 ,但实际上只需要遍历到 weight[ i ] 。

代码示例 :

  

#include <iostream>
#include <algorithm>
using namespace std ; int dp[1500] ; // 定义为全局变量 会被初始化为 0
int weight[15] ;
int value[15] ; int main ( ) {
int n , v ; cin >> n >> v ;
for ( int i = 1 ; i <= n ; i++ ) {
cin >> weight[i] >> value[i] ;
} for ( int i = 1 ; i <= n ; i++ ) {
for ( int j = v ; j >= weight[i] ; j-- ) { ////////// 修改处
dp[j] = max ( dp[j] , dp[j-weight[i]] + value[i] ) ; // 当面对一个物品时 , 此时数组所对应的即是前 i - 1 的价值
}
} cout << dp[v] << endl ; return 0 ;
}

背包的路径输出(板子)

int n,m;
int v[MAX],w[MAX];
int dp[MAX];
bool path[MAX][MAX];
int V;
void solve()
{
memset(dp,0,sizeof(dp));
memset(path,false,sizeof(path));
for(int i=0;i<n;i++)
{
for(int j=V;j>=w[i];j--)
if(dp[j-w[i]]+v[i]>dp[j])
{
dp[j]=dp[j-w[i]]+v[i];
path[i][j]=true;//cout<<i<<j<<endl;
}
}
cout<<dp[V]<<endl;
int ans[MAX];
int k=0;
for(int i=n-1;i>=0;i--)
{
if(path[i][V]){
ans[++k]=i;
V-=w[i];
}
}
//输出所选择的物品
for(int i=k;i>0;i--)
cout<<ans[i]<<endl;
}

DP- 01背包问题的更多相关文章

  1. 动态规划(DP),0-1背包问题

    题目链接:http://poj.org/problem?id=3624 1.p[i][j]表示,背包容量为j,从i,i+1,i+2,...,n的最优解. 2.递推公式 p[i][j]=max(p[i+ ...

  2. 采药 水题 dp 01背包问题 luogu1048

    最基本的01背包,不需要太多解释,刚学dp的同学可以参见dd大牛的背包九讲,直接度娘“背包九讲”即可搜到 #include <cstdio> #include <cstring> ...

  3. dp 0-1背包问题

    0-1背包的状态转换方程 f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ),  f[i-1,j] } f[i,j]表示在前i件物品中选择若干件放在承重为 j 的背包 ...

  4. 01背包问题之2(dp)

    01背包问题之2 有n个物品,重量和价值分别为wi和vi,从这些物品中挑选出重量不超过W的物品,求所有挑选方案中物品价值总和的最大值 限制条件: 1 <= n <= 100; 1 < ...

  5. 普通01背包问题(dp)

    有n个物品,重量和价值分别为wi和vi,从这些物品中挑选出重量不超过W的物品,求所有挑选方案中物品价值总和的最大值 限制条件: 1 <= n <= 100; 1 <= wi,vi & ...

  6. 01背包问题的延伸即变形 (dp)

    对于普通的01背包问题,如果修改限制条件的大小,让数据范围比较大的话,比如相比较重量而言,价值的范围比较小,我们可以试着修改dp的对象,之前的dp针对不同的重量限制计算最大的价值.这次用dp针对不同的 ...

  7. PAT 甲级 1068 Find More Coins (30 分) (dp,01背包问题记录最佳选择方案)***

    1068 Find More Coins (30 分)   Eva loves to collect coins from all over the universe, including some ...

  8. DP动态规划之01背包问题

    目录 问题描述 问题分析 问题求解 Java代码实现 优化方向一:时间方面:因为是j是整数是跳跃式的,可以选择性的填表. 思考二:处理j(背包容量),w(重量)不为整数的时候,因为j不为整数了,它就没 ...

  9. DP:0-1背包问题

    [问题描述] 0-1背包问题:有 N 个物品,物品 i 的重量为整数 wi >=0,价值为整数 vi >=0,背包所能承受的最大重量为整数 C.如果限定每种物品只能选择0个或1个,求可装的 ...

  10. 0-1背包问题-DP

    中文理解: 0-1背包问题:有一个贼在偷窃一家商店时,发现有n件物品,第i件物品价值vi元,重wi磅,此处vi与wi都是整数.他希望带走的东西越值钱越好,但他的背包中至多只能装下W磅的东西,W为一整数 ...

随机推荐

  1. CCPC2018 桂林 G "Greatest Common Divisor"(数学)

    UPC备战省赛组队训练赛第十七场 with zyd,mxl G: Greatest Common Divisor 题目描述 There is an array of length n, contain ...

  2. P1011 圆柱体的表面积

    题目描述 输入底面半径 \(r\) 和高 \(h\) ,输出圆柱体的表面积,保留 \(3\) 位小数. 输入格式 输入包含两个实数 \(r,h(1 \le r,h \le 1000)\) 且保证输入的 ...

  3. html2canvas生成图片报错处理办法

    详见大佬博客链接: link.(https://www.jianshu.com/p/22bd5b98e38a) 需要注意的是要生成的网页中带的网络图片地址(如放在阿里云服务器图库的图片)经常有跨域报错 ...

  4. git 上传当前分支

    因为我现在的分支是的名很长,每次需要上次当前分支需要写很多代码,是不是有很简单方法上传当前分支. 如果要上传一个分支到仓库 origin 那么就需要使用下面的命令 git push origin 分支 ...

  5. 使用idea构建Hibernate5项目

    使用工具:IntelliJ IDEA 2017.2.5 x64 MySql-8.0.1 hibernate-release-5.3.1.Final 导入的jar包: 以及连接MySql的jdbc包.和 ...

  6. 安装 Daloradius

    安装Mariadb yum install -y mariadb-server mariadb systemctl start mariadb systemctl enable mariadb 初始设 ...

  7. python类中的一些神奇方法

    __str__:用于在print(对象)时,直接打印__str__的返回值 class Animal: def __init__(self, name): self.name = name def _ ...

  8. Java程序员必备:异常的十个关键知识点

    前言 总结了Java异常十个关键知识点,面试或者工作中都有用哦,加油. 一. 异常是什么 异常是指阻止当前方法或作用域继续执行的问题.比如你读取的文件不存在,数组越界,进行除法时,除数为0等都会导致异 ...

  9. 20191024-3 互评Alpha阶段作品——扛把子组

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2019fall/homework/9860 基于NABCD评论作品,及改进建议 1.根据(不限于)NABCD评 ...

  10. 【转载】JavaScript术语:shim 和 polyfill

    在学习和使用 JavaScript 的时候,我们会经常碰到两个术语:shim 和 polyfill.它们有许多定义和解释,意思相近又有差异. Shim Shim 指的是在一个旧的环境中模拟出一个新 A ...