[经典] 背包问题(三)

【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. 最优方案的总数,声明一个记录数目的矩阵,在普通动规过程中,加一个判断,如果跳过,选择,或者两者都有,则进行相应的加法

6. 第K优解,转变一下:状态f[i][v]就应该是一个大小为K的数组f[i][v][1..K],有序队列f[i-1][v]即f[i-1][v][1..K],f[i-1][v-c[i]]+w[i]则理解为在f[i-1][v-c[i]][1..K]的每个数上加上w[i]后得到的有序队列,合并这两个有序队列并将结果的前K项储存到f[i][v][1..K]中的复杂度是O(K)。最后的答案是f[N][V][K]。总的复杂度是O(VNK)。

注意:背包问题的动态规划解法是个伪多项式解法,O(VN)与V有关,V巨大时,是没法求解的。

子集和问题是一个NP-Complete问题,与前述的(加权的)01背包问题并不相同。给定一个整数的集合S和一个整数X,问是否存在S的一个子集满足其中所有元素的和为X。这个问题有一个时间复杂度为O(2^(N/2))的较高效的搜索算法,其中N是集合S的大小。第一步思想是二分。将集合S划分成两个子集S1和S2,它们的大小都是N/2。对于S1和S2,分别枚举出它们所有的2^(N/2)个子集和,保存到某种支持查找的数据结构中,例如hash set。然后就要将两部分结果合并,寻找是否有和为X的S的子集。事实上,对于S1的某个和为X1的子集,只需寻找S2是否有和为X-X1的子集。假设采用的hash set是理想的,每次查找和插入都仅花费O(1)的时间。两步的时间复杂度显然都是O(2^(N/2))。实践中,往往可以先将第一步得到的两组子集和分别排序,然后再用两个指针扫描的方法查找是否有满足要求的子集和。这样的实现,在可接受的时间内可以解决的最大规模约为N=42。

搜索还是DP?

首先,可以从数据范围中得到命题人意图的线索。如果一个背包问题可以用DP解,V一定不能很大,否则O(VN)的算法无法承受,而一般的搜索解法都是仅与N有关,与V无关的。所以,V很大时(例如上百万),命题人的意图就应该是考察搜索。另一方面,N较大时(例如上百),命题人的意图就很有可能是考察动态规划了。另外,当想不出合适的动态规划算法时,就只能用搜索了。例如看到一个从未见过的背包中物品的限制条件,无法想出DP的方程,只好写搜索以谋求一定的分数了。

时间: 2024-08-29 21:46:18

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

菜鸟系列之C/C++经典试题(三)

设计包含min函数的栈 题目:定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素.要求函数min.push以及pop的时间复杂度都是O(1). 分析:这是2006年google的一道面试题. 我看到这道题目时,第一反应就是每次push一个新元素时,将栈里所有逆序元素排序.这样栈顶元素将是最小元素.但由于不能保证最后push进栈的元素最先出栈,这种思路设计的数据结构已经不是一个栈了. 在栈里添加一个成员变量存放最小元素(或最小元素的位置).每次push一个新元素进栈的时候,如果该元素比

编程算法 - 背包问题(三种动态规划) 代码(C)

背包问题(三种动态规划) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目參考: http://blog.csdn.net/caroline_wendy/article/details/37912949 能够用动态规划(Dynamic Programming, DP)求解, 能够通过记忆化搜索推导出递推式, 能够使用三种不同的方向进行求解. 动态规划主要是状态转移, 须要理解清晰. 代码: /* * main.cpp * * Created o

人脸识别经典算法三:Fisherface(LDA)

Fisherface是由Ronald Fisher发明的,想必这就是Fisherface名字由来.Fisherface所基于的LDA(Linear Discriminant Analysis,线性判别分析)理论和特征脸里用到的PCA有相似之处,都是对原有数据进行整体降维映射到低维空间的方法,LDA和PCA都是从数据整体入手而不同于LBP提取局部纹理特征.如果阅读本文有难度,可以考虑自学斯坦福公开课机器学习或者补充线代等数学知识. 同时作者要感谢cnblogs上的大牛JerryLead,本篇博文基

[经典] 背包问题(一)

[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处理问题会

概率统计——讲透最经典的三种概率分布

本文始发于个人公众号:TechFlow 这一讲当中我们来探讨三种经典的概率分布,分别是伯努利分布.二项分布以及多项分布. 在我们正式开始之前,我们先来明确一个概念,我们这里说的分布究竟是什么? 无论是在理论还是实际的实验当中,一个事件都有可能有若干个结果.每一个结果可能出现也可能不出现,对于每个事件而言出现的可能性就是概率.而分布,就是衡量一个概率有多大. 伯努利分布 明确了分布的概念之后,我们先从最简单的伯努利分布开始. 伯努利分布非常简单,就是假设一个事件只有发生或者不发生两种可能,并且这两

用面向对象的方式解决经典的“三月兔”的问题

这是一个经典问题:有一对兔子,从出生后第三个月开始,每个月都生一对兔子,然后生出来的兔子也从出生第三个月开始每个月生一对兔子,假如兔子都不死,问每个月兔子总数是多少. 这个问题其实是一个斐波纳切数列,主要考虑递归的用法,这道题的"标准答案"就是用递归的方式来解决: 然而,这么做其实并不是按面向对象的思路来解决的.这是先画出表格,然后找出数字的排列规律,然后再写公式. 对于这种问题,我还是喜欢用面向对象的思路来解决它. 如果要以面向对象的思路来做,应该写一个兔子的对象,然后根据它的繁殖规

PV 操作经典例题---三个进程之间的同步

问题: 总共有 读入.执行.打印 三个进程,试用PV操作描述读入B1打印B2的同步过程. 问题解读: 这个问题就是说了这样一件事:一个输入B1,被操作之后,成为B2,将B2打印.怎样用PV操作来说这件事.那么新的问题来了:啥是个PV操作? 就拿这道题来解释PV操作吧,我想打印一个值,前提条件是这个值存在吧,如果不存在,那么打印这个动作就不应该被执行,不能说啥也没有在那瞎打印吧.那么怎样才能让它不执行呢? 这就需要引入信号量机制了: 当一个操作的信号量为负数的话,就会挂起等待,不执行. 当一个操作

JS 入门经典 第三章 判断、循环和函数

1.比较运算符 在所有的比较运算符中,==和!=的优先级最低,而>.<.<=.>=则具有相同的优先级 所有的比较运算度的优先级都比算数运算符要低,所以先执行算数运算符,在执行比较运算符. 2.字符串的比较 JS将按照顺序依次的比较左右两边的字符串在同一位置的字符,以检查两个字符是否相等,一旦遇到不等的情况,就停止比较并且返回false.JS中字符串的比较是区分大小写的 3.一般使用整数作为循环计数器的. 4.for...in循环语句主要用于数组类型和对象类型的数据.它的特点是:可以

算法竞赛入门经典(三)

例题3-1 开灯问题 题目:有n盏灯,编号为1~n.第1个人把所有灯打开,第2个人按下所有编号为2的倍数的开关(这些灯将被关掉),第3个人按下所有编号为3的倍数的开关(其中关掉的灯将被打开,开着的灯将被关闭),依此类推.一共k个人,问最后有那些灯开着?输入:n和k,输出开着的灯编号.k<=n<=1000. 分析:用sign[1],sign[2],...,sign[n]表示编号为1,2,3,...,n的灯是否开着.模拟这些操作即可: 源码: #include<stdio.h> #in