动态规划——背包问题入门

动态规划——背包问题入门


1. 01背包


概述

给出N个物品,每个物体都具有一定的体积和价值,而每个物体都只有一个。 拥有一个V体积的背包,问应如何装包才能使背包中物体的总价值最大?

解题思路

背包问题是典型的动态规划类题目,而动态规划是典型的通过规律找出正解的方法。所以解题思路的关键在于如何寻找不同数据之间的关系(状态转移)。

直接描述不方便解释,我们以例题为例:

(注:01背包问题不涉及因为物体的形状、大小等而如何放入背包的问题,只是单纯考虑体积和价值。我们可以理解成每个物体都是液体,而这个背包是个大水缸(当然,每种液体要么全倒进去,要么一点不倒,不允许倒一部分。))



例:HDU2602 Bone Collector

Problem

Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?

简单释义

很久以前,在Teddy的家乡有一个被称作 “骨头收集者” 的人。这个人喜欢收集各式各样的骨头 —— 狗的,牛的,当然他也经常会去墓地转转......(是个狼灭)
这个人有一个总容量为V的背包,而在他的旅途中他有很多骨头要收集。而很明显,不同的骨头有着不同的体积和不同的价值。现在给定他找到的每个骨头都是什么样的,请计算这人能装进包里、收集到的骨头的最大价值是多少?

Input

The first line contain a integer T , the number of cases.
Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.

输入格式

输入第一行包含一个整数T,代表不同情况的数量(多组数据)。
跟随的T种情况一个情况有三行,第一行包含两个整数N、V(N<=1000,V<=1000)代表骨头的数量和他背包的容量。第二行包含N个整数代表每块骨头的价值。第三行包含N个整数代表每块骨头的体积。

Output

One integer per line representing the maximum of the total value (this number will be less than \(2^{31}\)).

输出格式

每行一个整数,代表总价值的最大值(这个数字不大于\(2^{31}\))。

输入样例

1
5 10
1 2 3 4 5
5 4 3 2 1

输出样例

14

题目分析

动态规划,即要求在各种可能的局部的解中,找到那些可能达到最优的解的局部解并找到最优解,而放弃其他的解,以此实现“大事化小,小事化了”。
以本题来说,这个狼灭装包有成千上万种方法,就有成千上万种局部解,而我们要找到的是价值最大的那一种,而这一种是成千上万种方法中的一种,也就是局部解的一种,而这一种是最优解。

我们给一组数据:

1
3 5
5 12 20
1 2 4

首先我们可以这样保存数据:

这个狼灭的每个骨头的体积是v[b],价值是w[b],给每个骨头编个号就是b,在前面已经声明好了:

    int T;
    scanf("%d",&T);
    for(int c=1;c<=T;c++)
    {
        int N,V;
        scanf("%d%d",&N,&V);
        for(int b=1;b<=N;b++)
            scanf("%d",&w[b]);
        for(int b=1;b<=N;b++)
            scanf("%d",&v[b]);
        }

而他选择要如何装包的过程,就是他 做决策 的过程。

先来看他拿的第一块骨头:
这块骨头的价值是5,体积是1。
我们只有这一块骨头,那么无论我们的背包有多大,我们都只能放这一块骨头进去。那么就有这样的关系:

总价值 1 2 3 4 5 6 7 ...... 背包体积
1 5 5 5 5 5 5 5 ......
骨头编号

现在这个狼灭又捡来了他的第二块骨头:那么他的背包体积要是比3大的话,他就可以装进两块骨头了:

——
他的背包体积为1,他只能放一块骨头1,价值是5,这也是最大价值;
他的背包体积为2,他可以放一块骨头2,价值是12,是最大价值;
他的背包体积为3,他可以放一块骨头2和一块骨头1,价值是17,是最大价值;
他的背包体积为4,他仍然只可以放一块骨头2和一块骨头1,价值是17,价值最大;
接下来他的背包体积再怎么大,他也只能放一块骨头2和一块骨头1,所以往后价值都是17:

总价值 1 2 3 4 5 6 7 ...... 背包体积
1 5 5 5 5 5 5 5 ......
2 5 12 17 17 17 17 17 ......
骨头编号

接下来他搞来了他的第三块骨头:体积4,价值20。

——
他的背包体积为1,他只能放一块骨头1,价值是5,这也是最大价值;
他的背包体积为2,他可以放一块骨头2,价值是12,是最大价值;
他的背包体积为3,他可以放一块骨头2和一块骨头1,价值是17,是最大价值,到这和前面都一样。
他的背包体积为4,他就可以放一块骨头3了,那价值是20,最大了;
他的背包体积为5,他可以放一块骨头3外加一块骨头1,这是25;
他的背包体积为6,他可以放一块骨头3外加一块骨头2,这是32;
他的背包体积为7,他可以放一块骨头3、一块骨头2、一块骨头1,那么就是37;
再往后的话,他无论如何也只能装这3块骨头了,往后都是37。

总价值 1 2 3 4 5 6 7 ...... 背包体积
1 5 5 5 5 5 5 5 ......
2 5 12 17 17 17 17 17 ......
3 5 12 17 20 25 32 37 ......
骨头编号

……

既然有了这么个表,我们便可以把这个表记下来。我们来一个数组:

int dp[i][j];//在这里,表示考虑到前i块骨头,背包体积为j,可以获得的最大价值。那么dp[i][j]就是这种情况下的最大价值。

那么这个数组就是:

dp[i][j] 1 2 3 4 5 6 7 ...... j
1 5 5 5 5 5 5 5 ......
2 5 12 17 17 17 17 17 ......
3 5 12 17 20 25 32 37 ......
i

乍一看好像没什么规律,而接下来就是关键——在这个表中找出数据的规律,这也就是动态规划的精华:推导状态转移方程 ——也就是这个狼灭做决策的过程。

然而这个狼灭拿到了一块骨头i,他需要考虑的问题只不过是他要不要把这块骨头i放进包。
他要是决定不放,那么他决策完了就是:dp[i][j]=dp[i-1][j];
——包里东西没变,和[i-1][j]时候的包是一样的;

他要是决定放,那么他决策完了就是:dp[i][j]=dp[i-1][j-v[i]]+w[i];
——他往包里放了一个i号骨头,那么这时候包里就有了这块i号骨头,而体积也被占去了v[i],但价值多了w[i]——于是反过来想,他放进去之后的dp[i][j]——--》就是他没有放i、体积是j-v[i]时候的那个状态加上i的价值《--!!

(简单的东西拿字母表示出来就有些烧脑了。。。QAQ)

于是他要是决定不放,那么状态[i][j]就是从[i-1][j]那里转移过来的;
他要是决定放,那么状态[i][j]就是从[i-1][j-v[i]]那里转移过来的;
他放不放都行,而我们要找的是价值最大的,所以这个时候的状态就是——

dp[i][j]=max(dp[i-1][j-v[i]]+w[i],dp[i-1][j]);//Note:j>=v[i];

这就是这道题的状态转移方程,接下来我们只消得把它塞进程序里就OK了:

#include<cstdio>
#include<algorithm> //max()的头
using namespace std;
int dp[1080][1080];
int w[1080],v[1080];
int main()
{
    int T;
    scanf("%d",&T);
    for(int c=1;c<=T;c++)
    {
        int N,V;
        scanf("%d%d",&N,&V);
        for(int b=1;b<=N;b++)
            scanf("%d",&w[b]);
        for(int b=1;b<=N;b++)
            scanf("%d",&v[b]);
        for(int i=1;i<=N;i++)//i个物品
            for(int j=0;j<=V;j++)//背包体积为j——转两个循环列举各个情况的状态,并用不同的状态进行转移:
            {
                if(j>=v[i])//使用状态转移方程
                    dp[i][j]=max(dp[i-1][j-v[i]]+w[i],dp[i-1][j]);
                if(j<v[i])//如果此时背包的体积比这个物体的体积小,那无论如何也放不进去,所以走“不放”的状态转移
                    dp[i][j]=dp[i-1][j];
            }
        printf("%d\n",dp[N][V]);
    }
    return 0;
}

……



可以看出来了:他要决定怎么放,那么他的决策有千万多种多,在这里面找出最优状态当然会累S。
而我们从他只有1块骨头开始,推导出两块骨头、三块骨头……并找出其中的联系,就可以找到他有N块骨头应该怎么放,那么我们便很容易在不断的转移过程中找出最优解,这也就是动态规划的“大事化小,小事化了”思维的精华所在。



动态规划——背包问题入门

原文地址:https://www.cnblogs.com/izwb003/p/dynamic-programming_knapsack-problem.html

时间: 2024-08-10 15:09:31

动态规划——背包问题入门的相关文章

nyist oj 289 苹果 (动态规划——背包问题)

苹果 时间限制:3000 ms  |  内存限制:65535 KB 难度:3 描述 ctest有n个苹果,要将它放入容量为v的背包.给出第i个苹果的大小和价钱,求出能放入背包的苹果的总价钱最大值. 输入 有多组测试数据,每组测试数据第一行为2个正整数,分别代表苹果的个数n和背包的容量v,n.v同时为0时结束测试,此时不输出.接下来的n行,每行2个正整数,用空格隔开,分别代表苹果的大小c和价钱w.所有输入数字的范围大于等于0,小于等于1000. 输出 对每组测试数据输出一个整数,代表能放入背包的苹

oj算法----动态规划----背包问题

oj算法----动态规划----背包问题 1.动态规划 1.1概念 动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法 1.2性质 动态规划一般用来处理最优解的问题.使用动态规划算法思想解决的问题一般具有最优子结构性质和重叠子问题这两个因素. <1> 最优子结构 一个问题的最优解包含其子问题的最优解,这个性质被称为最优子结构性质 <2> 重叠子问题 递归算法求解问题时,每次产生的子问题并不总是新问

01背包 -- 动态规划的入门题目

<strong><span style="font-size:18px;">首先说什么是动态规划: 经常听到 DP: Dynamic Programming的缩写 这里的入门题是这样的: 01背包 有重量与价值分别为Wi 和 Vi的 n 个物品.请从中选出物品,在重量综合不超过w的前提下,求出价值最大的. 样例: input: n = 4 (w, v) = {(2.3), (1, 2), (3, 4), (2, 3)} W = 5 outtput: 7(选择的是

动态规划——背包问题

背包问题是一类非常典型的动态规划问题,包括多种类型(01背包.完全背包.多重背包.混合背包.二维费用背包等)其基本类型为01背包问题. 一.01背包问题 N件物品,每件物品的重量和价值分别为 w[i], v[i], 把这些物品放到一个容量为W的背包中,求背包中物品的价值的最大值. 形式化定义:  分析     最直观的思路是枚举背包中出现的所有可能的物品组合,然后计算它们的价值和,求最大值.如果直接采用递归搜索,则会有大量的冗余,通过记忆化搜索的方式可以进行改进(记忆 max[i][w], 前i

动态规划-背包问题 Knapsack

2018-03-15 13:11:12 背包问题(Knapsack problem)是一种组合优化的NP完全问题.问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高.问题的名称来源于如何选择最合适的物品放置于给定背包中. 相似问题经常出现在商业.组合数学,计算复杂性理论.密码学和应用数学等领域中. 一.0/1背包问题 背包问题是个NPC问题,01背包可以通过动态规划算法在伪多项式时间内给出解. 0/1背包问题的特点是,每种物品仅仅

背包问题入门(单调队列优化多重背包

背包问题 写这篇文章主要是为了帮帮新人吧,dalao勿喷.qwq 一般的背包问题问法 每种物品都有一个价值w和体积c.//这个就是下面的变量名,请看清再往下看. 你现在有一个背包容积为V,你想用一些物品装背包使得物品总价值最大. 01背包 多种物品,每种物品只有一个.求能获得的最大总价值. 我们考虑是否选择第i件物品时,是需要考虑前i-1件物品对答案的贡献的. 分析 如果我们不选择第i件物品,那我们就相当于是用i-1件物品,填充了体积为v的背包所得到的最优解. 而我们选择第i件物品的时候,我们要

动态规划——背包问题python实现(01背包、完全背包、多重背包)

目录 01背包问题 完全背包问题 多重背包问题 参考: 背包九讲--哔哩哔哩 背包九讲 01背包问题 01背包问题 描述: 有N件物品和一个容量为V的背包. 第i件物品的体积是vi,价值是wi. 求解将哪些物品装入背包,可使这些物品的总体积不超过背包流量,且总价值最大. 二维动态规划 f[i][j] 表示只看前i个物品,总体积是j的情况下,总价值最大是多少. result = max(f[n][0~V]) f[i][j]: 不选第i个物品:f[i][j] = f[i-1][j]; 选第i个物品:

动态三角形(动态规划思想入门)

个人心得:动态规划是一种隶属于决策学的一个算法思想,他能够很好的解决多阶段决策问题,这种思想对于我们的生活还是科研都是必不可少的, 需要好生体会,学会动态方程的转移,做到具体问题具体分析. 那这简单题目来看吧,动态三角形,很明显从第一个开始就可以看出来第一个等于下面俩个对角线中最大与自己相加,所以可以用递归完成, 当然还有种更加巧妙的就是从后面往前面走,你可以很明显看到从倒数第二行开始他的最大值应该等于下俩个对角线中的最大值加上自己本身. 所以方程可以这么表示 DP[i][j]=max(DP[i

动态规划-背包问题

1.P1060 开心的金明 https://www.luogu.org/problemnew/solution/P1164 #include <stdio.h> #include <memory.h> #include <math.h> #include <string> #include <vector> #include <set> #include <stack> #include <queue> #in