简单的背包问题

背包问题动态规划中非常经典的一个问题,本文只包含01背包,完全背包和多重背包。更加详尽的背包问题的讲解请参考崔添翼大神的《背包九讲》

简单的01背包

  • 问题导入:新年到了,mjl马上就要外出旅游。mjl拥有一个容量为P的小背包,他希望在自己的n件体积为Vi的物品中带走的物品体积之和尽可能的多,他最多能带走多少物品?(每件物品只有一个)
  • 问题分析:可以创建一个二维数组dp[i][j],使用0和1表示对于前i件物品是否能凑出j的体积。要判断dp[i][j]的值是否为true,可以查看dp[i-1][j-V[i]]即前i-1件物品是否能凑出j-V[i]的重量出来或者dp[i-1][j]即前i-1件物品已经可以把体积j凑出来。
  • 代码实现:
    memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i=1; i<=n; i++)
{
for(int j=p; j>=v[i]; j--)
{
if(dp[i-1][j-v[i]] || dp[i-1][j])
{
dp[i][j] = 1;
}
}
}
int ans;
for(int i=p; i>=0; i--)
{
if(dp[n][j])
{
ans = j;
break;
}
}

注意代码中从后向前更新dp[i][j],是为了防止一件物品被使用多次

对于简单01背包问题的优化

显然,在简单01背包问题中,空间复杂度达到了O(n*p)之多。而在每一次更新第i行的数据时,只需要i-1行的数据即可,再向上的数据完全可以舍弃,所以只需要开一个两行的二维数组即可。

01滚动:

  • 只存在第0行和第一行,每次更新数据后将第1行的数据复制到第0行
  • 代码实现:
    memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i=1; i<=n; i++)
{
for(int j=p; j>=v[i]; j--)
{
if(dp[0][j-v[i]] || dp[0][j])
{
dp[1][j] = 1;
}
}
for(int i=0; i<=n; i++)
{
dp[0][i] = dp[1][i];
}
}
int ans;
for(int i=p; i>=0; i--)
{
if(dp[1][j])
{
ans = j;
break;
}
}

尽管01滚动的空间复杂度已经优化了很多,但是每一次结束后的复制操作增加了时间复杂度。同时最终对于结果有用的只是最后一行,因此有一种优化空间的同时不影响时间复杂度的优化方法,即使用一位数组就地滚动

就地滚动:

  • 只使用一个一位数组,每一次在原来数组的基础上更新数据
  • 代码实现:
    memset(dp,0,sizeof(dp));
dp[0] = 1;
for(int i=1; i<=n; i++)
{
for(int j=p; j>=v[i]; j--)
{
if(dp[j-v[i]] == 1)
{
dp[j] = 1;
}
}
}
int ans;
for(int i=p; i>=0; i--)
{
if(dp[i] == 1)
{
ans = i;
break;
}
}

01背包

  • 问题导入:既然外出旅游,当地的纪念品当然是必不可少的,因此mjl来到了景区内的一家纪念品商店。众所周知,景区的商店往往都是黑店,mjl身上的钱并不能买下他喜欢的所有东西,他对于每一件纪念品做了一个评分,评分越高代表他越喜欢这件纪念品,现在mjl想知道他能带走商品的最大评分和是多少,其中价格为p[i],评分为v[i],携带的钱为m。
  • 问题分析:这里只需要使用一个二维数组dp[i][j]表示对于前i个物品,当已花费j金钱时能够带走的最大价值。状态转移方程为dp[i][j] = max( dp[i-1][j] , dp[i-1][j-p[i]] + v[i] )。

    -代码实现(就地滚动):
    memset(dp,0,sizeof(dp));
for(int i=1; i<=n; i++)
{
for(int j=m; j>=p[i]; j--)
{
dp[j] = max(dp[j], dp[j-p[i]]+v[i]);
}
}
int ans = 0;
for(int i=m; i>=0; i--)
{
ans = max(ans, dp[i]);
}
printf("%d\n",ans);

完全背包

  • 完全背包简单的说就是在01背包的基础上,所有的物品都可以重复购买无限次。既然在01背包中为了防止一件物品被多次使用而从后向前遍历,那么在完全背包中只需要改为从前向后遍历即可
  • 代码实现:
    memset(dp,0,sizeof(dp));
for(int i=1; i<=n; i++)
{
for(int j=p[i]; j<=m; j++)
{
dp[j] = max(dp[j], dp[j-p[i]]+v[i]);
}
}
int ans = 0;
for(int i=m; i>=0; i++)
{
ans = max(ans, dp[i]);
}
printf("%d\n",ans);

未完待续……

【动态规划】【C/C++】简单的背包问题的更多相关文章

  1. 详解动态规划(Dynamic Programming)& 背包问题

    详解动态规划(Dynamic Programming)& 背包问题 引入 有序号为1~n这n项工作,每项工作在Si时间开始,在Ti时间结束.对于每项工作都可以选择参加与否.如果选择了参与,那么 ...

  2. CJOJ 2022 【一本通】简单的背包问题(搜索)

    CJOJ 2022 [一本通]简单的背包问题(搜索) Description 设有一个背包可以放入的物品重量为S,现有n件物品,重量分别是w1,w2,w3,-wn. 问能否从这n件物品中选择若干件放入 ...

  3. 简单的背包问题(入门)HDU2602 HDU2546 HDU1864

    动态规划,我一直都不熟悉,因为体量不够,所以今天开始努力地学习学习. 当然背包从01开始,先选择了一个简单的经典的背包HDU2602. Many years ago , in Teddy's home ...

  4. HDU 1561&HDU 3449 一类简单依赖背包问题

    HDU 1561.这道是树形DP了,所谓依赖背包,就是选A前必须选B,这样的问题.1561很明显是这样的题了.把0点当成ROOT就好,然后选子节点前必须先选根,所以初始化数组每一行为该根点的值.由于多 ...

  5. 51nod 1270 数组的最大代价 思路:简单动态规划

    这题是看起来很复杂,但是换个思路就简单了的题目. 首先每个点要么取b[i],要么取1,因为取中间值毫无意义,不能增加最大代价S. 用一个二维数组做动态规划就很简单了. dp[i][0]表示第i个点取1 ...

  6. 动态规划:HDU2159-FATE(二维费用的背包问题)

    FATE Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

  7. 背包问题(01背包,完全背包,多重背包(朴素算法&&二进制优化))

    写在前面:我是一只蒟蒻~~~ 今天我们要讲讲动态规划中~~最最最最最~~~~简单~~的背包问题 1. 首先,我们先介绍一下  01背包 大家先看一下这道01背包的问题  题目  有m件物品和一个容量为 ...

  8. LFYZ-OJ ID: 1028 背包问题

    背包问题 题目描述 简单的背包问题.设有一个背包,可以放入的重量为s.现有n件物品,重量分别为w1,w2-,wn,(1≤i≤n)均为正整数,从n件物品中挑选若干件,使得放入背包的重量之和正好为s.找到 ...

  9. 【模板】各种背包问题&讲解

                                        背包问题集合 一般来说,动态规划(DP)都是初学者最难闯过的一关,而在这里详细解说动态规划的一种经典题型:背包问题. 这里介绍的 ...

随机推荐

  1. C#中 ref 关键字的认识和理解

    之前接手老项目的时候有遇到一些的方法参数中使用了ref关键字加在传参的参数前面的情况.对于新手,这里介绍和讲解一下ref的用法和实际效果. CLR中默认所有方法的参数传递方式都是传值,也就是说不管你传 ...

  2. #《Essential C++》读书笔记# 第四章 基于对象的编程风格

    基础知识 Class的定义由两部分组成:class的声明,以及紧接在声明之后的主体.主体部分由一对大括号括住,并以分号结尾.主体内的两个关键字public和private,用来标示每个块的" ...

  3. 部署基于Gitlab+Docker+Rancher+Harbor的前端项目这一篇就够了

    部署基于Gitlab+Docker+Rancher+Harbor的前端项目这一篇就够了 安大虎 ​ momenta 中台开发工程师 6 人赞同了该文章 就目前的形势看,一家公司的运维体系不承载在 Do ...

  4. javascript中如何使用js脚本模拟"request"获取url后参数值呢?

    转自:猫猫小屋--js获取url后参数信息 摘要: 下文讲述javascript中使用js代码获取url地址后面的参数值的方法分享,如下所示: 实现思路: 使用正则表达式对参数值进行匹配,获取参数后的 ...

  5. AGC011-E Increasing Numbers

    题意 给定一个数\(n\),\(n≤10^{500,000}\),问\(n\)最少可以拆分成几个不降数的和.一个不降数是在十进制位下,从高位往低位看,每个数都不会比高位的数更小的数 做法 不降数可以拆 ...

  6. app简单压力测试

    step1:手机开发者选项中,将USB调试选上 step2:确认手机和电脑已经连接成功:adb devices step3:安装测试app:adb install package.apk (1)cd命 ...

  7. Django复制记录的方法

    最近的Django项目中有复制记录的需求.数据库里有一张名为Party的表,记录用户创建的party,现在要让用户能够复制一个新的party.本身非常简单的一个功能,但运行的时候出错了.我以为是复制过 ...

  8. CSS操作

    CSS 与 JavaScript 是两个有着明确分工的领域,前者负责页面的视觉效果,后者负责与用户的行为互动.但是,它们毕竟同属网页开发的前端,因此不可避免有着交叉和互相配合. 1. 使用JavaSc ...

  9. java script 内置对象

    java script 内置对象 Date 日期对象 字符串对象 定义字符串的方法就是直接赋值 使用 String 对象的 toUpperCase() 方法来将字符串小写字母转换为大写,反之 toLo ...

  10. P4197 Peaks [克鲁斯卡尔重构树 + 主席树][克鲁斯卡尔重构树学习笔记]

    Problem 在\(Bytemountains\)有\(n\)座山峰,每座山峰有他的高度\(h_i\) .有些山峰之间有双向道路相连,共\(M\)条路径,每条路径有一个困难值,这个值越大表示越难走, ...