背包九讲 附:USACO中的背包问题
附:USACO中的背包问题
USACO是USA Computing Olympiad的简称,它组织了很多面向全球的计算机竞赛活动。
USACO Trainng是一个很适合初学者的题库,我认为它的特色是题目质量高,循序渐进,还配有不错的课文和题目分析。其中关于背包问题的那篇课文 (TEXT Knapsack Problems) 也值得一看。
另外,USACO Contest是USACO常年组织的面向全球的竞赛系列,在此也推荐NOIP选手参加。
我整理了USACO Training中涉及背包问题的题目,应该可以作为不错的习题。其中标加号的是我比较推荐的,标叹号的是我认为对NOIP选手比较有挑战性的。
题目列表
- Inflate (+) (基本01背包)
- Stamps (+)(!) (对初学者有一定挑战性)
- Money
- Nuggets
- Subsets
- Rockers (+) (另一类有趣的“二维”背包问题)
- Milk4 (!) (很怪的背包问题问法,较难用纯DP求解)
题目简解
以下文字来自我所撰的《USACO心得》一文,该文的完整版本,包括我的程序,可在DD的USACO征程中找到。
Inflate 是加权01 背包问题,也就是说:每种物品只有一件,只可以选择放或者不放;而且每种物品有对应的权值,目标是使总权值最大或最小。它最朴素的状态转移方程是:f[k][i] = max{f[k-1][i] , f[k-1][i-v[k]]+w[k]}。f[k][i]表示前k 件物品花费代价i 可以得到的最大权值。v[k]和w[k]分别是第k 件物品的花费和权值。可以看到, f[k]的求解过程就是使用第k 件物品对f[k-1]进行更新的过程。那么事实上就不用使用二维数组,只需要定义f[i],然后对于每件物品k,顺序地检查f[i]与f[i-v[k]]+w[k]的大小,如果后者更大,就对前者进行更新。这是背包问题中典型的优化方法。
题目stamps 中,每种物品的使用量没有直接限制,但使用物品的总量有限制。求第一个不能用这有限个物品组成的背包的大小。(可以这样等价地认为)设f[k][i] 表示前k 件物品组成大小为i 的背包, 最少需要物品的数量。则f[k][i]= min{f[k-1][i],f[k-1][i-j*s[k]]+j},其中j 是选择使用第k 件物品的数目,这个方程运用时可以用和上面一样的方法处理成一维的。求解时先设置一个粗糙的循环上限,即最大的物品乘最多物品数。
Money 是多重背包问题。也就是每个物品可以使用无限多次。要求解的是构成一种背包的不同方案总数。基本上就是把一般的多重背包的方程中的min 改成sum 就行了。
Nuggets 的模型也是多重背包。要求求解所给的物品不能恰好放入的背包大小的最大值(可能不存在)。只需要根据“若i、j 互质,则关于x、y 的不定方程i*x+y*j=n 必有正整数解,其中n>i*j”这一定理得出一个循环的上限。 Subsets 子集和问题相当于物品大小是前N 个自然数时求大小为N*(N+1)/4 的 01 背包的方案数。
Rockers 可以利用求解背包问题的思想设计解法。我的状态转移方程如下: f[i][j][t]=max{f[i][j][t-1] , f[i-1][j][t] , f[i-1][j][t-time[i]]+1 , f[i-1][j-1][T]+(t>=time[i])}。其中 f[i][j][t]表示前i 首歌用j 张完整的盘和一张录了t 分钟的盘可以放入的最多歌数,T 是一张光盘的最大容量,t>=time[i]是一个bool 值转换成int 取值为0 或1。但我后来发现我当时设计的状态和方程效率有点低,如果换成这样:f[i][j]=(a,b)表示前i 首歌中选了j 首需要用到a 张完整的光盘以及一张录了b 分钟的光盘,会将时空复杂度都大大降低。这种将状态的值设为二维的方法值得注意。
Milk4 是这些类背包问题中难度最大的一道了。很多人无法做到将它用纯DP 方法求解,而是用迭代加深搜索枚举使用的桶,将其转换成多重背包问题再DP。由于 USACO 的数据弱,迭代加深的深度很小,这样也可以AC,但我们还是可以用纯DP 方法将它完美解决的。设f[k]为称量出k 单位牛奶需要的最少的桶数。那么可以用类似多重背包的方法对f 数组反复更新以求得最小值。然而困难在于如何输出字典序最小的方案。我们可以对每个i 记录pre_f[i]和pre_v[i]。表示得到i 单位牛奶的过程是用pre_f[i]单位牛奶加上若干个编号为pre_v[i]的桶的牛奶。这样就可以一步步求得得到i 单位牛奶的完整方案。为了使方案的字典序最小,我们在每次找到一个耗费桶数相同的方案时对已储存的方案和新方案进行比较再决定是否更新方案。为了使这种比较快捷,在使用各种大小的桶对f 数组进行更新时先大后小地进行。USACO 的官方题解正是这一思路。如果认为以上文字比较难理解可以阅读官方程序或我的程序。
Copyright (c) 2007 Tianyi Cui
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation.
整理 by stntwm
完全背包问题优化分析
无限背包,最容易想到的自然是(所谓无限背包,意思是说,物品可以无限地选取,而不是只能选择一个)
(1)定义F(i,j)为i时间前j个物品的最优解可知
F(i,j)=max{ F(k,j-1)+(i -k ) div t[j]*w[j] } (0<=k<=i)
此种算法复杂度接近O(n^3),很不理想,考虑到可以不枚举分配的时间而是枚举
选择多少个j物品,可以得到一个较上式为优化的方程.
F(i,j)=max{ F(i-t[j]*k,j-1)+k*w[j] } (0<=k<=i mod t[j] );
这样得到进一步优化.但是这样依然要TLE第二个数据.
另外一个问题是直接开数据将要MLE.对于这类方程,我们用的最多的办法,最容易理解的办法就是滚动数据,滚动数组可以用mod来实现,也可以开两个一维数组变量之后交替复制,就是在每次递归之前多加一句g:=f;其中F是当前数组,G是上一数组.
但事实上,可以只用一个一维数组,关键在于递推的顺序性,一般是这样实现的.!!下面的代码没有经过debug或者make,而且仅仅是一般的背包问题的伪代码1 for(int j=1;j<=n;j++)
2 for(int i=m;i>=0;i--)
3 if ((t[j]<=i)&&(f[i-t[j]]+w[j]>f[i]))
4 f[i]=f[i-t[j]]+w[j];
结论:在每进行一次第一行的循环节(就是2-4行)后,数组f中f就是前j个物品在i时间时的最优解.为什么?当代码尚未开始执行,f显然满足这样的性质,每次循环时我们用第j个物品尝试加入到原来i时间的方案中,如果发现当前解更优,就更新f中的值.要注意其中i的循还顺序,当解问题f(i,j)时,我们需要问题f(k,j-1)的解,其中k<=i,换言之,在上述代码中,我们解决f这一子问题的时候需要保证f[k](k<=i)的数据还是未更新过的数据.如果把i的循环顺序掉转,会得到怎么样的结果呢?下面有分析.
(在这样的实现方案中,第二行的i可以简单地至t[j]停止循环)
由以上的思路,对于这道题目,我们得到了这样的代码
1 for j:=1 to n do
2 for i:=m downto t[j] do
3 tmp1:=i;
4 tmp2:=0;
5 for k:=1 to i div t[j] do
6 dec(tmp1,t[j]);
7 inc(tmp2,w[j]);
8 tmp:=f[tmp1]+tmp2;
9 if tmp>f then f:=tmp;
但是即使是这样的代码,在第二个数据的面前还是要TLE.....
最后我们发现,对于无限背包,每个物品可以取到无数次,换言之,我们只希望最后解最优,而不考虑是否解里面有多个相同的物品.也就是说,我们可以把第j个物品加到已经加过第J物品的解上.别的分析和一般的01背包是一致的,关键在于如何反复地检验能否加入第J个物品.
回想一般的01背包的代码,我们在代码第2行让i从m循环到t[j],以此来保证第J个物品将会加到一个尚没有出现过该物品的方案中,那么,如果我们反过来循环呢?
1 for j:=1 to n do
2 for i:=t[j] to m do
3 if (t[j]<=i)and(f[i-t[j]]+w[j]>f then
4 f:=f[i-t[j]]+w[j];
此时,第J个物品便有可能加入到一个已经有该物品的最优解中,正好满足完全背包问题的要求.
(完)
背包九讲 附:USACO中的背包问题的更多相关文章
- My背包九讲——概述
文章目录 什么是背包问题 背包问题的分类 [第一讲 01背包问题](https://blog.csdn.net/qq_34261446/article/details/103705068) 第二讲 完 ...
- 【DP_背包专题】 背包九讲
这段时间看了<背包九讲>,在HUST VJUDGE上找到了一个题单,挑选了其中16道题集中做了下,选题全部是HDU上的题,大多是简单题.目前做了点小总结,大概提了下每道题的思路重点部分,希 ...
- 背包九讲PDF
本资料仅限个人学习交流使用,不得用于商业用途. 背包九讲PDF:https://pan.baidu.com/s/17rTxMwCo9iSTOW77yucdXQ 提取码:xbqa
- 背包九讲 && 题目
★.背包求方案数的时候,多重背包是不行的,因为产生重复的背包会有多种情况. ★.背包记录路径的时候,其实是不行的,因为更新了12的最优解,如果它依赖于6这个背包,然后你后面改变了6这个背包,就GG 1 ...
- dd大牛的《背包九讲》
P01: 01背包问题 题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路 这是最 ...
- 直接抱过来dd大牛的《背包九讲》来做笔记
P01: 01背包问题 题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路 这是最 ...
- 摘自 dd大牛的《背包九讲》
P01: 01背包问题 题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路 这是最 ...
- 背包九讲(Orz)
P01: 01背包问题 题目 有\(N\)件物品和一个容量为\(V\)的背包.第\(i\)件物品的费用是\(c[i]\),价值是\(w[i]\).求解将哪些物品装入背包可使这些物品的费用总和不超过背包 ...
- dd 在度娘上看到的一个大牛的《背包九讲》 (:
P01: 01背包问题 题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路 这是最 ...
随机推荐
- This application failed to start because it could not find or load the Qt platform plugin "xcb".
1. copy libQt5DBus.so.5 2. add QT_PLUGIN_PATH blog.csdn.net/windows_nt/article/details/242 ...
- extjs MVC模式的个人看法
针对一个后台管理页面是mvc模式,后台也是mvc模式下的项目,要怎么去熟悉呢? 首先以我个人的认解,先从后台的管理界面来看,会有control,model,store,view:其中先看view的代码 ...
- 安装SQL Server出现在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke的错误解决办法
以下是错误报告: 标题: SQL Server 安装程序失败. ------------------------------ SQL Server 安装程序遇到以下错误: 在创建窗口句柄之 ...
- linux实现c多进程
线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者.传统的Unix也支持线程的概念,但是在一个进程(process)中只允许 ...
- [LeetCode] Edit Distance(很好的DP)
Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2 ...
- JavaScript学习基础部分
JavaScript学习基础 一.简介 1.JavaScript 是因特网上最流行的脚本语言,并且可在所有主要的浏览器中运行,比方说 Internet Explorer. Mozilla.Firefo ...
- linux命令之echo
echo可以用来输出显示变量和常量,还可以用来写文件. 最简单的方式:使用 echo 命令 #echo abcbedf>>a.txt 将abcdef追加到a.txt文件末尾 往文件中写入内 ...
- h5固定表头公共样式
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scal ...
- 3.Python编程语言基础技术框架
3.Python编程语言基础技术框架 3.1查看数据项数据类型 type(name) 3.2查看数据项数据id id(name) 3.3对象引用 备注Python将所有数据存为内存对象 Python中 ...
- 密码有效性验证失败。该密码不够复杂,不符合 Windows 策略要求
我在sqlserver2005中建立一个用户的时候,我的用户名和密码是一样的,它不允许我建立报“密码有效性验证失败.该密码不够复杂,不符合 Windows 策略要求”错误,我把密码改成其他一些就可以, ...