29 背包问题

理论说明

本节将要讨论动态规划问题中又一个十分常见,同时在机试中又是终点被考察的问题--背包问题。背包问题的变化之多让我们不容易一下子完全掌握它,根据考研机试的实际需要,我们在本节中主要讨论0-1背包、完全背包和多重背包这三类背包问题。

题目来源

2008年北京大学图形学实验室计算机研究生机试真题

题目描述

辰辰是个很有潜能、天资聪颖的孩子,他的梦想是称为世界上最伟大的医师。 为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。 医师把他带到个到处都是草药的山洞里对他说: “孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。 我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。” 如果你是辰辰,你能完成这个任务吗?

输入说明

输入的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),T代表总共能够用来采药的时间,M代表山洞里的草药的数目。 接下来的M行每行包括两个在1到100之间(包括1和100)的的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出说明

可能有多组测试数据,对于每组数据, 输出只包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

样例展示

输入:
70 3
71 100
69 1
1 2
输出:
3

题目分析

我们可以将这个问题抽象:有一个容量为V的背包,和一些问题。这些物品分别有两个属性,体积w和价值v,每种物品只有一个。要求这个背包装下价值尽可能多的物品,求该最大价值,背包可以不被装满。

因为最优解中,每个物品都有两种可能的情况,即在背包中或者不在背包中,所以我们将这个问题称为0-1背包问题。

在众多方案中求解最优解,是典型的动态规划问题。为了用动态规划来解决该问题,我们用dp[i][j]表示在总体积不超过j的情况下,前i个物品所能达到的最大价值。初始时,dp[0][j]为0.依据每种物品是否被放入背包,每个状态有两个状态转移的来源。若物品i被放入背包中,设其体积为w,价值为v,则dp[i][j]=dp[i-1][j-w]+v。即在总体积不超过j-w时前i-1件物品可组成的最大价值的基础上再加上i物品的价值v;若物品i不被放入背包中,则dp[i][j]=dp[i-1][j],即此时与总体积不超过j的钱i-1件物品组成的价值最大值等价。选择他们之间的最大值称为状态dp[i][j]的值。综上所述,0-1背包的状态转移方程为:

  dp[i][j]=max(dp[i-1][j-w]+v,dp[i-1][j]);
  注意:j-w的值是否为负数,若为负,则不能转移

C++代码

#include<iostream>
using namespace std;

int max(int a,int b) {
    return a>b? a:b;
}

struct E{
    int w;
    int v;
}list[101];

int dp[101][101];
int s,n;
int main() {
    //dp[i][j]表示在总体积不超过j的情况下,前i个物品所能达到的最大价值。
    while(scanf("%d%d",&s,&n)!=EOF) {
        for(int i=1;i<=n;i++) {
            scanf("%d%d",&list[i].w,&list[i].v);
        }
        for(int i=0;i<=s;i++) {
            dp[0][i]=0;
        }
        for(int i=1;i<=n;i++) { //玄幻每一个物品
            for(int j=s;j>=list[i].w;j--) { //确保j-list[i]>=0
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-list[i].w]+list[i].v);
            }
            for(int j=list[i].w-1;j>=0;j--) {
                dp[i][j]=dp[i-1][j];
            }
        }
        printf("%d",dp[n][s]);
    }
    return 0;
}

后续分析

观察状态转移的特点,我们发现dp[i][j]的转移仅仅与dp[i-1][j-list[i].w]和dp[i-1][j]有关,即仅仅与二维数组中本行的上一行有关。根据这个特点,我们将原来的二维数组优化成一维数组,并用如下方式完成状态转移:

dp[j]=max{dp[j-list[i].w]+v,dp[j]}

其中再本次跟新中未经修改的dp[j-list[i].w]和dp[j]与原始写法中的dp[i-1][j-list[i].w]和dp[i-1][j]等值。为了保证状态正确的转移,我们必须保证再每次跟新中确定状态dp[j]时,dp[j]和dp[j-list[i].w]未被本次跟新修改,考虑到j-list[i].w<j,那么在每次跟新中倒序遍历所有j的值,就能保证在确定dp[j]的值时,dp[j-list[i].w]的值尚未被修改,从而完成正确的状态转移

#include<iostream>
using namespace std;
#define INF 0x7fffffff

int max(int a,int b) {
    return a>b? a:b;
}

struct E {
    int w;
    int v;
}list[100];

int dp[1001];
int main() {
    int s,n;
    while(scanf("%d%d",&s,&n)!=EOF) {
        for(int i=1;i<=n;i++) {
            scanf("%d%d",&list[i].w,&list[i].v);
        }
        for(int i=0;i<=s;i++) {
            dp[i]=0;
        }
        for(int i=1;i<=n;i++) {
            for(int j=s;j>=list[i].w;j--) {
                dp[j]=max(dp[j],dp[j-list[i].w]+list[i].v);
            }
        }
        printf("%d\n",dp[s]);
    }
    return 0;
}

Piggy-Bank

题目描述

输入说明

输出说明

样例说明

问题分析

题目大意:有一个存储罐,告知为空时的重量和当前重量,并给定一些钱币的价值和相应的重量,求存储罐中最少有多少先进。

高校夏令营机试训练 文章被收录于专栏

Leetcode题目太多,不知道如何准备高校夏令营?欢迎关注本专栏,和本小白一起准备夏令营吧! 本专题的规划如下: 截止到4月下旬:以王道考研为依托,提供夏令营机考的准备计划,打好基础 截止到5月中旬:以剑指offer进一步加强 本专题的内容如下: 1. 给出题目的在线测试链接,方面您检查代码的正确性 2. 给出题解

全部评论

相关推荐

不愿透露姓名的神秘牛友
07-02 15:39
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
07-03 16:22
点赞 评论 收藏
分享
06-27 12:54
已编辑
门头沟学院 Java
累了,讲讲我的大学经历吧,目前在家待业。我是一个二本院校软件工程专业。最开始选专业是觉得计算机感兴趣,所以选择了他。本人学习计算机是从大二暑假结束开始的,也就是大三开始。当时每天学习,我个人认为Java以及是我生活的一部分了,就这样持续学习了一年半,来到了大四上学期末,大概是在12月中旬,我终于找的到了一家上海中厂的实习,但我发现实习生的工作很枯燥,公司分配的活也不多,大多时间也是自己在自学。就这样我秋招末才找到实习。时间来到了3月中旬,公司说我可以转正,但是转正工资只有7000,不过很稳定,不加班,双休,因为要回学校参加答辩了,同时当时也是心高气傲,认为可以找到更好的,所以放弃了转正机会,回学校准备论文。准备论文期间就也没有投递简历。然后时间来到了5月中旬,这时春招基本也结束了,然后我开始投递简历,期间只是约到了几家下场面试。工资也只有6-7k,到现在我不知道该怎么办了。已经没有当初学习的心劲了,好累呀,但是又不知道该干什么去。在家就是打游戏,boss简历投一投。每天日重一次。26秋招都说是针对26届的人,25怎么办。我好绝望。要不要参加考公、考研、央国企这些的。有没有大佬可以帮帮我。为什么感觉别人找工作都是顺其自然的事情,我感觉自己每一步都在艰难追赶。八股文背了又忘背了又忘,我每次都花很长时间去理解他,可是现在感觉八股、项目都忘完了。真的已经没有力气再去学习了。图片是我的简历,有没有大哥可以指正一下,或者说我应该走哪条路,有点不想在找工作了。
码客明:太累了就休息一下兄弟,人生不会完蛋的
如果实习可以转正,你会不...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务