[经典] 背包问题(一)

【1】01背包

N个物品,占容c[i],价值w[i],放入1个容量为V的背包,使得总价值最大

分析:每种物品仅有一件,可以选择放或不放

转移方程:opt[i][v] = max{opt[i - 1][v], opt[i - 1][v - c[i]] + w[i]}

复杂度:时间空间均为O(NV),空间复杂度可压缩,用opt[v]表示,但需要注意的是v必须从V到0遍历,否则逻辑错误;

初始化技巧:如果要求刚好装满,则设为负无穷;如果只要求最大,则设为0即可。

事实上,由于对某件物品的01处理问题会在不同情景下都被调用,所以可以写成一个调用函数ZeroOnePack(cost, weigth),其中v的下限可被优化

procedure ZeroOnePack(cost,weight)     
    for v=V..cost 
        f[v]=max{f[v],f[v-cost]+weight} 

所以本题的伪代码可以写成

for i=1..N 
    ZeroOnePack(c[i],w[i]);

另外,其实下限可以进一步被优化,当V很大时有效,此时本题的伪代码可以写成

for i=1..n 
    bound=max{V-sum{w[i..n]},c[i]}     
    for v=V..bound            f[v]=max{f[v],f[v-cost]+weight} 

【2】完全背包

特点是每件物品的数目都是没有限制的,可以由01背包延展出解法,即f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]}, 0<=k*c[i]<=v;复杂度很显然上升了O(v/c[i]),所以尝试找到优化的方法。最简单的优化:c[i] < c[j] && w[i] > w[j],则显然应该选择w[i],去除j物品。

先是最小均分,将每个物品当成有v/c[i]个,则完全背包等效于01背包,复杂度仍比01上升O(v/c[i]);然后是指数拆分,将每个物品拆分成费用为c[i]*2^k、价值为w[i]*2^k的子物品,则复杂度只上升O(log(v/c[i]))。

经典解法:复杂度与01背包一样,只有O(VN)。动态规划方程为f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]},而空间复杂度为O(V)的算法只需要将v从0到V顺序遍历。

procedure CompletePack(cost,weight)     
    for v=cost..V 
        f[v]=max{f[v],f[v-c[i]]+w[i]}

事实上,v循环与i循环的次序可以颠倒

【3】多重背包

特点是每件物品的数目有中庸的限制,即第i件物品的数目限制是n[i]。最直接的方法也是拆分01背包,f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]}, 0<=k<=n[i],复杂度为O(V*Σn[i]);然后是指数拆分,也是经典解法,复杂度为O(V*Σlog n[i])。

procedure MultiplePack(cost,weight,amount)     
    if cost*amount>=V 
        CompletePack(cost,weight)         
        return     
    integer k=1     
    while k<amount 
        ZeroOnePack(k*cost,k*weight)         
        amount=amount-k        
        k=k*2 
        ZeroOnePack(amount*cost,amount*weight)

还有复杂度更低的方法,复杂度与01背包一样,只有O(VN),方法是单调队列优化,超了NOIP范围。

【4】混合背包

将前三种背包过程分类混合使用,伪代码:

for i=1..N 
    if 第i件物品属于01背包        
        ZeroOnePack(c[i],w[i])     
    else if 第i件物品属于完全
        CompletePack(c[i],w[i])    
    else if 第i件物品属于多重背包         
        MultiplePack(c[i],w[i],n[i])         

【5】二维费用背包

多用一个状态记录多出来的费用,设f[i][v][u]表示前i件物品付出两种代价分别为v和u时可获得的最大价值。状态转移方程就是:

f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]} 
时间: 2024-08-02 05:41:12

[经典] 背包问题(一)的相关文章

[经典] 背包问题(三)

[9]背包问题的实际应用 1. 简单扩展,加上“最多可以放多少件物品或者最多可以装满多少背包的空间”类似的限制条件,同样可以先求出动态规划矩阵值,然后再在里面搜 2. 打印出最优方案,则需要对每一步动态规划过程,记录下它的选择g[i][v] 3. 输出字典序最小,N到1输入,需要注意的是,如果跳过i与选择i结果一样时,应该选择i 4. 装满背包或将背包装至某一指定容量的方案总数,f[i][v]=sum{f[i-1][v],f[i][v-c[i]]},初始条件f[0][0]=1 5. 最优方案的总

DP总结 ——QPH

常见优化 单调队列 形式 dp[i]=min{f(k)} dp[i]=max{f(k)} 要求 f(k)是关于k的函数 k的范围和i有关 转移方法 维护一个单调递增(减)的队列,可以在两头弹出元素,一头压入元素. 队列中维护的是两个值.一个是位置,这和k的范围有关系,另外一个是f(k)的值,这个用来维护单调性,当然如果f(k)的值可以利用dp值在O(1)的时间内计算出来的话队列中可以只维护一个表示位置的变量. 枚举到一个i的时候,首先判断队首元素的位置是否已经不满足k的范围了,如果不满足就将队首

状态压缩---UVA6625 - Diagrams &amp; Tableaux

比赛的时候刷出来的第一个状态DP.(期间有点没有把握是状态DP呢.) 题意:题意还是简单的.K行的方格.之后输入L1~LK 代表每一行方格数.在这些往左紧挨的方格子里填上1~N的数字. 其中右边格子的数值会大于等于左边的格子,下边的格子的数值会大于上边的格子. 其中观察一列的数值.会发现一列的数值均是不一样的.而且 N<=7.也就是说我们让1~N的数字填到第一列上.那我们可以按列来进来状态压缩.也就是认为一列就是一个状态. 也就是我们让: 数值 7 6 5 4 3 2 1 二进制0 0 0 0

HDU-2844-Coins(多重背包)

Problem Description Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. One day Hibix opened purse and found there were some coins. He decided to buy a very nice watch in a nearby shop. He wanted to pay the exact price(witho

动态规划算法实例三则

动态规划属于不好理解的计算机基本算法之一. 需要经过多次实践,才能体会其精妙之处. 其精妙的地方在于:降低运算量. 下面通过实例理解动态规划解题思路. 实例一:求数组的最大连续和子数组.参考文章 用动态规划来解,首先得考虑状态和状态转移方程.如果我们把题述数组看成序列,那么是不是可以用序列DP来考虑呢? 我们不妨考虑一个这样的序列:1,-3,5,-2,4 a[i]表示这个序列的第 i 个元素,dp[i]表示最后一个元素是a[i]的最大连续和(此乃状态,是不是跟LIS的DP解法有点类似),于是:

J - I NEED A OFFER! &lt;HDU 1203&gt;

J - I NEED A OFFER! Speakless很早就想出国,现在他已经考完了所有需要的考试,准备了所有要准备的材料,于是,便需要去申请学校了.要申请国外的任何大学,你都要交纳一定的申请费用,这可是很惊人的.Speakless没有多少钱,总共只攒了n万美元.他将在m个学校中选择若干的(当然要在他的经济承受范围内).每个学校都有不同的申请费用a(万美元),并且Speakless估计了他得到这个学校offer的可能性b.不同学校之间是否得到offer不会互相影响."I NEED A OFF

[动态规划] leetcode 416 Partition Equal Subset Sum

problem:https://leetcode.com/problems/partition-equal-subset-sum/ 经典背包问题.找到是否存在恰好装满sum / 2的物体,可以优化为1D的. class Solution { public: bool canPartition(vector<int>& nums) { int n = nums.size(); int sum = accumulate(nums.begin(), nums.end(), 0); if (s

01背包问题(空间优化)经典代码

题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使价值总和最大. 基本思路 这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放. 用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值.则其状态转移方程便是: f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]} 这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的.所以有必要将它详细解释一下

动态规划经典题解--背包问题

1.完全背包--背包不允许剩余 #include <iostream> #include <string.h> #define N 50002 #define M 2002 using namespace std; //测试OJ:nyoj 311 /* 背包不允许剩余,与允许剩余相比,只需将d[i]初始为负无穷大,d[0]=0 d[i]: 用去i容量时的最大价值 */ int d[N]; struct Node { int pri; int vol; }c[M]; int main