DP- 01背包问题
这个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背包问题的更多相关文章
- 动态规划(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+ ...
- 采药 水题 dp 01背包问题 luogu1048
最基本的01背包,不需要太多解释,刚学dp的同学可以参见dd大牛的背包九讲,直接度娘“背包九讲”即可搜到 #include <cstdio> #include <cstring> ...
- 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 的背包 ...
- 01背包问题之2(dp)
01背包问题之2 有n个物品,重量和价值分别为wi和vi,从这些物品中挑选出重量不超过W的物品,求所有挑选方案中物品价值总和的最大值 限制条件: 1 <= n <= 100; 1 < ...
- 普通01背包问题(dp)
有n个物品,重量和价值分别为wi和vi,从这些物品中挑选出重量不超过W的物品,求所有挑选方案中物品价值总和的最大值 限制条件: 1 <= n <= 100; 1 <= wi,vi & ...
- 01背包问题的延伸即变形 (dp)
对于普通的01背包问题,如果修改限制条件的大小,让数据范围比较大的话,比如相比较重量而言,价值的范围比较小,我们可以试着修改dp的对象,之前的dp针对不同的重量限制计算最大的价值.这次用dp针对不同的 ...
- 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 ...
- DP动态规划之01背包问题
目录 问题描述 问题分析 问题求解 Java代码实现 优化方向一:时间方面:因为是j是整数是跳跃式的,可以选择性的填表. 思考二:处理j(背包容量),w(重量)不为整数的时候,因为j不为整数了,它就没 ...
- DP:0-1背包问题
[问题描述] 0-1背包问题:有 N 个物品,物品 i 的重量为整数 wi >=0,价值为整数 vi >=0,背包所能承受的最大重量为整数 C.如果限定每种物品只能选择0个或1个,求可装的 ...
- 0-1背包问题-DP
中文理解: 0-1背包问题:有一个贼在偷窃一家商店时,发现有n件物品,第i件物品价值vi元,重wi磅,此处vi与wi都是整数.他希望带走的东西越值钱越好,但他的背包中至多只能装下W磅的东西,W为一整数 ...
随机推荐
- [转]如何让多个不同类型的后端网站用一个nginx进行反向代理实际场景分析
前段时间公司根据要求需要将聚石塔上服务器从杭州整体迁移到张家口,刚好趁这次机会将这些乱七八糟的服务器做一次梳理和整合,断断续续一个月迁移完 成大概优化掉了1/3的机器,完成之后遇到了一些问题,比如曾今 ...
- Python 数据类型,常用函数方法分类
Python基本数据类型:(int) 字符串(str)列表(list)元组(tuple)字典(dict)布尔(bool) python中可以简单使用 类型(数据)创建或转换数据 例: #字符串转数字 ...
- Java开发之快捷键
1.显示桌面快捷键:win+D或者右击状态栏,选择显示桌面. 2.UE编辑器:如果想把多行记录合并为一行,使用替换(Ctrl+R),查找里输入^p(代表回车换行符),替换为里什么都不填,替换位置选择所 ...
- win10 uwp 使用 LayoutTransformer
如果需要使用旋转,那么很容易把图片旋转的布局被裁剪.如果需要旋转的控件还在指定的 Grid 内,就需要使用布局的旋转.本文告诉大家如何使用 LayoutTransformer.需要知道,uwp是没有 ...
- linux 不用 ioctl 的设备控制
有时控制设备最好是通过写控制序列到设备自身来实现. 例如, 这个技术用在控制台驱动 中, 这里所谓的 escape 序列被用来移动光标, 改变缺省的颜色, 或者进行其他的配置任 务. 这样实现设备控制 ...
- 【50.00%】【codeforces 747C】Servers
time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...
- react + webpack 多页面搭建
一.利用 creat-react-app 新建一个react单页面应用. cnpm i -g create-react-app create-react-app demo cd demo npm st ...
- activiti工作流引擎学习(二)
1.连线 如果bpmn文件和java文件放在同一目录下,需要配置buildPath: 使用流程变量,设置连线需要的流程变量的名称,并设置流程变量的值,流程会按照指定的连线完成任务. 1.1一个活动中可 ...
- vc得到屏幕的当前分辨率方法
vc得到屏幕的当前分辨率方法:1.Windows API调用int width = GetSystemMetrics ( SM_CXSCREEN ); int height= GetSystemMet ...
- Codeforces Round #587 C. White Sheet(思维+计算几何)
传送门 •题意 先给一个白矩阵,再两个黑矩阵 如果两个黑矩阵能把白矩阵包含,则输出NO 否则输出YES •思路 计算几何题还是思维题呢? 想起了上初中高中做几何求面积的题 这个就类似于那样 包含的话分 ...