背包问题

第九讲 背包问题问法的变化

若如初见. 提交于 2019-11-29 09:37:26
以上涉及的各种背包问题都是要求在背包容量(费用)的限制下求可以取到的最大价值,但背包问题还有很多种灵活的问法,在这里值得提一下。但是我认为,只要深入理解了求背包问题最大价值的方法,即使问法变化了,也是不难想出算法的。 例如,求解最多可以放多少件物品或者最多可以装满多少背包的空间。这都可以根据具体问题利用前面的方程求出所有状态的值(f数组)之后得到。 还有,如果要求的是“总价值最小”“总件数最小”,只需简单的将上面的状态转移方程中的max改成min即可。 下面说一些变化更大的问法。 输出方案 一般而言,背包问题是要求一个最优值,如果要求输出这个最优值的方案,可以参照一般动态规划问题输出方案的方法:记录下每个状态的最优值是由状态转移方程的哪一项推出来的,换句话说,记录下它是由哪一个策略推出来的。便可根据这条策略找到上一个状态,从上一个状态接着向前推即可。 还是以01背包为例,方程为 f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]} 。再用一个数组g[i][v],设g[i][v]=0表示推出f[i][v]的值时是采用了方程的前一项(也即f[i][v]=f[i-1][v]),g[i][v]表示采用了方程的后一项。注意这两项分别表示了两种策略:未选第i个物品及选了第i个物品。那么输出方案的伪代码可以这样写(设最终状态为f[N][V]): 另外

背包九讲,写给自己

我只是一个虾纸丫 提交于 2019-11-28 22:47:00
之前学了dp没有好好看一遍背包九讲,今天把背包九讲过一遍,供之后自己看方便一些。 一. 01背包    题目链接:https://www.acwing.com/problem/content/2/   n,V<=1000    这个没什么好说的,加滚动数组,复杂度O(nV),代码如下: #include<cstdio> #include<algorithm> using namespace std; const int maxn=1e3+5; int n,V,dp[maxn],v[maxn],w[maxn]; int main(){ scanf("%d%d",&n,&V); for(int i=1;i<=n;++i) scanf("%d%d",&v[i],&w[i]); for(int i=1;i<=n;++i) for(int j=V;j>=v[i];--j) dp[j]=max(dp[j],dp[j-v[i]]+w[i]); printf("%d\n",dp[V]); return 0; } View Code   要注意的是,如果题目限制一定要装满,那么应将dp数组初始化为负无穷,只将dp[0]初始化0,此时dp[i]的定义是背包大小为i装满的最大价值,其余都一样,答案是dp[V]。 二. 完全背包    题目链接:https://www.acwing.com/problem

单调队列优化多重背包问题 + 例题

元气小坏坏 提交于 2019-11-28 18:44:09
例题: https://www.acwing.com/problem/content/6/ 多重背包除了可以使用二进制加速,还可以使用单调队列加速,并且单调队列会更快 正常的多重背包的dp方程: 其中 k 为物品的多少, v[i]为物品的体积, w[i]为物品的价值 可以看到转移的时候,dp[i][j]只和dp[i][j-k*v[i]]有关,并且是取动态的固定区间大小的区间最值,可以考虑使用优先队列优化。 使用优先队列要求我们j由小到大遍历,因此此题不方便使用一维数组简化空间,但是可以使用滚动数组减少空间 (假如使用一维数组,那么之前计算过的数值就会无限叠加,如图j-3v会叠加到j 然后j再带着j-3v的值不断往前叠加) 使用滚动数组的时候,设 dp[2][i] i:表示此时背包体积多少 dp数组则表示当体积为i的时候可以获得的最优解 每次使用的时候dp[t][i] , 每次需要滚动的时候t^=1 即可 还有一个问题,就是优先队列如何维护数值。 假如直接维护dp[i][k] + (j-k)/v[i] * w[i] (也就是维护dp的值),那么i增加的时候,之前的dp对dp[i]的贡献全部会改变,比如: 当j+=v的时候 此时优先队列里维护的东西就变了。 所以需要维护一个不受距离影响的值,具体看代码: #include<bits/stdc++.h> using namespace

[bzoj1775][Usaco2009 Dec]Vidgame 电视游戏问题_背包dp

青春壹個敷衍的年華 提交于 2019-11-28 18:40:57
1775: [Usaco2009 Dec]Vidgame 电视游戏问题 题 目链接 : https://www.lydsy.com/JudgeOnline/problem.php?id=1775 题解 : 发现是个$zz$分组背包。 但是,正常的分组背包是,完全背包+01背包,在这里根本行不通因为数据范围。 故此我们考虑 背包 $dp$。 状态 :$f_{(i,j)}$表示前$i$组,$i$选,花费$j$的最大价值;$g_{(i,j)}$表示$i$不选。 因为空间开的下,所以我们可以把$i$也放进去。 不然$i$是可以滚掉的。 代码 : #include <bits/stdc++.h> using namespace std; int f[51][100001], g[51][100001]; char *p1, *p2, buf[100000]; #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ ) int rd() { int x = 0; char c = nc(); while (c < 48) { c = nc(); } while (c > 47) { x = (((x << 2) + x) << 1) + (c ^ 48

算法3:背包问题

邮差的信 提交于 2019-11-28 17:58:37
背包问题和01背包问题是很经典的关于动态规划和贪心算法的题目。 这两个问题很相似,01背包是有一个容量为c的背包,装入一些质量为w[ ]的且价值为v[ ]的物品,每次只能选择放入或者不放,不能只放一部分某个物品。求出可以让背包装最大价值的一个x[ ],其中的每一项表示第 i 个物品是否要装入。 背包问题跟01背包相似,但是可以装入部分商品。 背包问题可以用贪心算法求解,01背包则要用动态规划。 先来说01背包 举个例子: c=5,即背包的容量是5.放入以下质量和价值的物品 编号 质量w 价值v 0 1 6 1 3 12 2 2 10 根据前面的动态规划的解法,动态规划一般需要一个二维的数组存放每一步计算得到的动态结果,本例用矩阵 m 表示,行标表示商品编号 用 i 表示,列标表示变化的 j ,即容量 0 1 2 3 4 5 0 0 6 10 16 18 22 1 0 0 10 12 12 22 2 0 0 10 10 10 10 本例的最佳装法解x应该是x={0,1,1},也就是装入第二个和第三个,总最大价值为12+10=22 我写了一个简单的代码 1 #include<iostream> 2 using namespace std; 3 int m[3][6];//动态规划中的矩阵 4 int v[3]={12,10,6};//价值 5 int w[3]={3,2,1};//质量

回溯算法——0-1背包

北城以北 提交于 2019-11-28 17:57:59
回溯算法的思想:每到一个十字路口A,就选择一条路走a,如果a走不通,则回到十字路口A,选择其他bcd之一,进行走。若依然走不通,则退回到A之前的十字路口,重复上面的操作。 利用回溯算法解决的经典问题:数独、八皇后、0-1背包、图的着色、旅行商问题、全排列等等。 0-1背包问题 #include<iostream> #define MAX_WEIGHT 100 using namespace std; // arr 待装包的物品重量, // curweight 当前i物品的重量 // i 当前即将放入的i物品 // num 可用物品的数量 // bagweight 当前背包的总重量 void fill(int *arr, int curweight, int i, int num, int &bagweight) { if(curweight == MAX_WEIGHT || i == num) // 装满,或者考察完所有物品 { if(curweight > bagweight) { bagweight = curweight; // 记录历史最大装载量 // cout << bagweight << "***" << endl << endl; } return; } fill(arr, curweight, i + 1, num, bagweight); // 不选择i物品

背包问题综合

偶尔善良 提交于 2019-11-28 16:18:04
01背包 问题:若干个物体,每个价值为c[i]重量为w[i],数量均为1,背包最大容量为W,问怎样取物体才能最大化收益? 解法:dp[i][j]以j为容量为放入前i个物品(按i从小到大的顺序)的最大价值。有递推关系式:dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+c[i])。这里的第一维度可以省略。 注意这里的内存重量循环j为W到0,递减关系(保证每个物体取一遍)(后面的不会修改前面的) 当背包空间j少于当前物体的重量时不选该物体这一步可以省略,对后续没有影响。 最终写法为: 1 for(int i=1; i<=m; i++) //物品 2 for(int j=W; j>=0; j--) //容量 3 { 4 if(j >= w[i]) 5 dp[i][j] = max(dp[i-1][j-w[i]]+val[i], dp[i-1][j]); 6 else //只是为了好理解 7 dp[i][j] = dp[i-1][j]; 8 } 完全背包 问题:若干个物体,每个价值为c[i]重量为w[i],数量均为无限,背包最大容量为W,问怎样取物体才能最大化收益? 解法:dp[i][j]以j为容量为放入前i个物品(按i从小到大的顺序)的最大价值。有递推关系式:dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+c[i])

#(抽象背包的转化)洛谷P1282 多米诺骨牌(提高+/省选-)

假如想象 提交于 2019-11-28 13:47:27
题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点。现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|。例如在图8-1中,S1=6+1+1+1=9,S2=1+5+3+2=11,|S1-S2|=2。每个多米诺骨牌可以旋转180°,使得上下两个方块互换位置。 编程用最少的旋转次数使多米诺骨牌上下2行点数之差达到最小。 对于图中的例子,只要将最后一个多米诺骨牌旋转180°,可使上下2行点数之差为0。 输入格式 输入文件的第一行是一个正整数n(1≤n≤1000),表示多米诺骨牌数。接下来的n行表示n个多米诺骨牌的点数。每行有两个用空格隔开的正整数,表示多米诺骨牌上下方块中的点数a和b,且1≤a,b≤6。 输出格式 输出文件仅一行,包含一个整数。表示求得的最小旋转次数。 输入输出样例 输入 #1 复制 4 6 1 1 5 1 3 1 2 输出 #1 复制 1分析:这道题其实是一道十分抽象的背包问题(你在逗我?这那里是背包,明明是贪心好咩?(≧∇≦)ノ)真的是背包啊!QAQ那么我们如何调整呢?我们先把骨牌反转,使点数大的在上方;这样保证上方的点数和一定大于下方的点数和;那么翻转时的点数改变就是上下点数的差值乘以2了!想到这里,我们可以考虑到:一开始的上下的点数的差值抽象成背包的体积,每一个骨牌当成一个物品,因为我们一开始就把点数大的放在了上面

洛谷背包问题P2938 [USACO09FEB]股票市场Stock Market(完全背包的转化)

二次信任 提交于 2019-11-28 07:27:43
题目描述 尽管奶牛天生谨慎,它们仍然在住房抵押信贷市场中大受打击,现在它们准备在股市上碰碰运气。贝西有内部消息,她知道 S 只股票在今后 D 天内的价格。 假设在一开始,她筹集了 M 元钱,那么她该怎样操作才能赚到最多的钱呢?贝西在每天可以买卖多只股票,也可以多次买卖同一只股票,交易单位必须是整数,数量不限。举一个牛市的例子: 假设贝西有 10 元本金,股票价格如下: 股票 今天的价格 明天的价格 后天的价格 A A 10 15 15 B B 13 11 20 最赚钱的做法是:今天买入 A A股 1 张,到明天把它卖掉并且买入 B 股 1 张,在后天卖掉 B股,这样贝西就有 24 元了。 输入格式 第一行:三个整数 S, D 和 M, 2 ≤ S ≤ 50 2 ≤ S ≤ 5 0 ; 2 ≤ D ≤ 10 2 ≤ D ≤ 1 0 ; 1 ≤ M ≤ 200000 1 ≤ M ≤ 2 0 0 0 0 0 第二行到第 S + 1 行:第 i + 1 行有 D 个整数: P_{i;1} P i ; 1 ​ 到 P_{i;D} P i ; D ​ ,表示第 i i 种股票在第一天到最后一天的售价,对所有 1 ≤ j ≤ D 1 ≤ j ≤ D, 1 ≤ Pi 1 ≤ P i; j ≤ 1000 j ≤ 1 0 0 0 输出格式 单个整数:表示奶牛可以获得的最大钱数,保证这个数不会超过

背包问题

让人想犯罪 __ 提交于 2019-11-28 07:21:34
简介 背包问题是一类动态规划问题的统称,分有多种子类型。 01背包 给定 \(n\) 个物品,每个物品都有自己的价值 \(v_i\) 和重量 \(w_i\) 。现有一个容量为 \(W\) 的背包,求最大价值。 很容易想到每种物品只有选或者不选,那么依次枚举即可。 考虑到还需要判断能否装下这些物品,所以还需要在转移的时候维护剩余容量。 因此设子状态 \(f[i][j]\) 为当前在第 \(i\) 个物品处,包括 \(i\) 在内已经选了重量为 \(j\) 的物品的最大价值。 状态转移方程为 \(f[i][j]=max(f[i-1][j-v_i]+w[i],f[i-1][j])\) 此时的空间复杂度为 \(O(NM)\) ,可以通过滚动数组优化到 \(O(M)\) 。 事实上,还可以进行进一步优化: 由于每一层 \(i\) 之间是互相独立的(也就是说 \(f[i][j]\) 不会被 \(f[i][k]\) 更新),所以我们可以优化掉第一维,但是此时需要修改第二维的枚举顺序。 根据状态转移方程可知,第二维大的状态是由第二维小的状态转移来的,由于我们优化掉了第一维,所以必须先遍历第二维大的状态,否则将会出现覆盖的情况。 所以第一维遍历仍然顺序,第二维遍历须改为逆序。 来源: https://www.cnblogs.com/ilverene/p/11398617.html