├── README ├── pack.pdf ├── path.pdf ├── Makefile ├── README.md ├── path.lyx └── pack.lyx /README: -------------------------------------------------------------------------------- 1 | README.md -------------------------------------------------------------------------------- /pack.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyicui/DP-Book/HEAD/pack.pdf -------------------------------------------------------------------------------- /path.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyicui/DP-Book/HEAD/path.pdf -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LYX=/Applications/LyX.app/Contents/MacOS/lyx 2 | 3 | ALL: pack.pdf path.pdf 4 | 5 | %.pdf: %.lyx 6 | $(LYX) --export pdf4 $< 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 动态规划的思考艺术 2 | 3 | ## 已完成初稿的 4 | 5 | * 背包问题 6 | 7 | ## 正在写初稿的 8 | 9 | * 寻路问题 10 | 11 | ## 计划中的 12 | 13 | * 资源分配 14 | * 线性模型 15 | * 博弈类问题 16 | * 树形DP 17 | * ... 18 | -------------------------------------------------------------------------------- /path.lyx: -------------------------------------------------------------------------------- 1 | #LyX 2.0 created this file. For more info see http://www.lyx.org/ 2 | \lyxformat 413 3 | \begin_document 4 | \begin_header 5 | \textclass scrartcl 6 | \begin_preamble 7 | \usepackage{datetime} 8 | \renewcommand{\dateseparator}{-} 9 | 10 | \usepackage[BoldFont,SlantFont,CJKsetspaces,CJKchecksingle]{xeCJK} 11 | \setCJKmainfont[BoldFont=STSong,ItalicFont=STKaiti]{STSong} 12 | \setCJKsansfont[BoldFont=STHeiti]{STHeiti} 13 | \setCJKmonofont{STFangsong} 14 | \parindent 2em 15 | \end_preamble 16 | \use_default_options true 17 | \maintain_unincluded_children false 18 | \language chinese-simplified 19 | \language_package default 20 | \inputencoding auto 21 | \fontencoding global 22 | \font_roman default 23 | \font_sans default 24 | \font_typewriter default 25 | \font_default_family default 26 | \use_non_tex_fonts true 27 | \font_sc false 28 | \font_osf false 29 | \font_sf_scale 100 30 | \font_tt_scale 100 31 | 32 | \graphics none 33 | \default_output_format default 34 | \output_sync 0 35 | \bibtex_command default 36 | \index_command default 37 | \paperfontsize default 38 | \spacing single 39 | \use_hyperref true 40 | \pdf_title "背包问题九讲" 41 | \pdf_author "崔添翼" 42 | \pdf_bookmarks true 43 | \pdf_bookmarksnumbered true 44 | \pdf_bookmarksopen true 45 | \pdf_bookmarksopenlevel 2 46 | \pdf_breaklinks true 47 | \pdf_pdfborder true 48 | \pdf_colorlinks true 49 | \pdf_backref section 50 | \pdf_pdfusetitle true 51 | \papersize a4paper 52 | \use_geometry false 53 | \use_amsmath 1 54 | \use_esint 1 55 | \use_mhchem 1 56 | \use_mathdots 1 57 | \cite_engine basic 58 | \use_bibtopic false 59 | \use_indices false 60 | \paperorientation portrait 61 | \suppress_date false 62 | \use_refstyle 1 63 | \index Index 64 | \shortcut idx 65 | \color #008000 66 | \end_index 67 | \secnumdepth 3 68 | \tocdepth 3 69 | \paragraph_separation indent 70 | \paragraph_indentation default 71 | \quotes_language english 72 | \papercolumns 1 73 | \papersides 1 74 | \paperpagestyle default 75 | \tracking_changes false 76 | \output_changes false 77 | \html_math_output 0 78 | \html_css_as_file 0 79 | \html_be_strict false 80 | \end_header 81 | 82 | \begin_body 83 | 84 | \begin_layout Title 85 | 寻寻觅觅∙道阻且长 86 | \begin_inset Newline newline 87 | \end_inset 88 | 89 | 动态规划与寻路问题 90 | \begin_inset Newline newline 91 | \end_inset 92 | 93 | v0.3 preview 94 | \end_layout 95 | 96 | \begin_layout Author 97 | 崔添翼 (Tianyi Cui) 98 | \begin_inset Foot 99 | status open 100 | 101 | \begin_layout Plain Layout 102 | a.k.a. 103 | dd_engi 104 | \end_layout 105 | 106 | \end_inset 107 | 108 | 109 | \end_layout 110 | 111 | \begin_layout Date 112 | \begin_inset ERT 113 | status open 114 | 115 | \begin_layout Plain Layout 116 | 117 | 118 | \backslash 119 | yyyymmdddate 120 | \backslash 121 | today 122 | \end_layout 123 | 124 | \end_inset 125 | 126 | 127 | \begin_inset Foot 128 | status open 129 | 130 | \begin_layout Plain Layout 131 | Build 132 | \begin_inset ERT 133 | status open 134 | 135 | \begin_layout Plain Layout 136 | 137 | 138 | \backslash 139 | pdfdate 140 | \end_layout 141 | 142 | \end_inset 143 | 144 | 145 | \end_layout 146 | 147 | \end_inset 148 | 149 | 150 | \end_layout 151 | 152 | \begin_layout Standard 153 | 本文题为《寻寻觅觅∙道阻且长》,副题为《动态规划与寻路问题》,从属于《动态规划的思考艺术》系列。 154 | \end_layout 155 | 156 | \begin_layout Standard 157 | 本文的修订历史及最新版本请访问 158 | \begin_inset CommandInset href 159 | LatexCommand href 160 | target "https://github.com/tianyicui/DP-Book" 161 | 162 | \end_inset 163 | 164 | 查阅。 165 | \end_layout 166 | 167 | \begin_layout Standard 168 | 本文版权归原作者所有,采用 169 | \begin_inset CommandInset href 170 | LatexCommand href 171 | name "CC BY-NC-SA" 172 | target "http://creativecommons.org/licenses/by-nc-sa/3.0/" 173 | 174 | \end_inset 175 | 176 | 协议发布。 177 | \end_layout 178 | 179 | \begin_layout Standard 180 | 本文的这个版本并未公开发布,只由作者直接发布给若干试读者,试读者没有传播给别人的权利。若你并非作者指定的试读者,而通过其它途径读到本文,则说明存在至少一位试读者 181 | 未尽保密义务。 182 | \end_layout 183 | 184 | \begin_layout Standard 185 | \begin_inset CommandInset toc 186 | LatexCommand tableofcontents 187 | 188 | \end_inset 189 | 190 | 191 | \end_layout 192 | 193 | \begin_layout Section 194 | 寻路问题导引 195 | \end_layout 196 | 197 | \begin_layout Standard 198 | 本文所探讨的寻路问题是指:给定某个可抽象为图论中的模型的图,要求找到图中满足某种性质的一条路径。除判别存在性之外,一般均要求此条路径具备某种最优性,亦即在某种计 199 | 算方法下最短或最长。对于路径的起点和终点也常常有着限制。 200 | \end_layout 201 | 202 | \begin_layout Standard 203 | 寻路问题一般都是最优化问题,这一点上与动态规划是一致的。所以说,很多寻路类型的题目都可用动态规划解答,图论中的Dijkstra算法(见 204 | \begin_inset CommandInset ref 205 | LatexCommand ref 206 | reference "sub:Dijkstra算法" 207 | 208 | \end_inset 209 | 210 | )、Floyd算法(见 211 | \begin_inset CommandInset ref 212 | LatexCommand ref 213 | reference "sub:全图寻路的Floyd算法" 214 | 215 | \end_inset 216 | 217 | )都是动态规划在图论中的经典应用。 218 | \end_layout 219 | 220 | \begin_layout Standard 221 | 另一方面,很多原本不存在图论模型的动态规划题目,也可转化成或建立起图论模型,并利用图论中的寻路算法进行解答。例如完全背包问题(TODO:引用《背包问题九讲》的相 222 | 应章节),就可转化为图论模型:容量为 223 | \begin_inset Formula $V$ 224 | \end_inset 225 | 226 | 的背包转化为编号从 227 | \begin_inset Formula $0$ 228 | \end_inset 229 | 230 | 到 231 | \begin_inset Formula $V$ 232 | \end_inset 233 | 234 | 的点,每个费用为 235 | \begin_inset Formula $v_{i}$ 236 | \end_inset 237 | 238 | 、价值为 239 | \begin_inset Formula $w_{i}$ 240 | \end_inset 241 | 242 | 的物品转化为 243 | \begin_inset Formula $V-v_{i}+1$ 244 | \end_inset 245 | 246 | 条从 247 | \begin_inset Formula $k+v_{i}$ 248 | \end_inset 249 | 250 | 到 251 | \begin_inset Formula $k$ 252 | \end_inset 253 | 254 | 的长度为 255 | \begin_inset Formula $w_{i}$ 256 | \end_inset 257 | 258 | 的边( 259 | \begin_inset Formula $0\leq k\le V-v_{i}$ 260 | \end_inset 261 | 262 | )。而找到一组最优解则对应为找到这个有向无环图中点 263 | \begin_inset Formula $V$ 264 | \end_inset 265 | 266 | 到点 267 | \begin_inset Formula $0$ 268 | \end_inset 269 | 270 | 的一条最长路(具体算法见 271 | \begin_inset CommandInset ref 272 | LatexCommand ref 273 | reference "sub:有向无环图中的寻路" 274 | 275 | \end_inset 276 | 277 | )。 278 | \end_layout 279 | 280 | \begin_layout Standard 281 | 综上,寻路问题与动态规划有着千丝万缕的联系。本文后面的部分便着重探讨这种联系衍生出的千变万化的动态规划题目与解法。 282 | \end_layout 283 | 284 | \begin_layout Section 285 | 特殊图中的寻路 286 | \end_layout 287 | 288 | \begin_layout Subsection 289 | \begin_inset CommandInset label 290 | LatexCommand label 291 | name "sub:矩阵中的寻路" 292 | 293 | \end_inset 294 | 295 | 矩阵中的寻路 296 | \end_layout 297 | 298 | \begin_layout Subsubsection 299 | 基础问题 300 | \end_layout 301 | 302 | \begin_layout Standard 303 | 问题是这样的:设有一 304 | \begin_inset Formula $M\times N$ 305 | \end_inset 306 | 307 | 的矩阵 308 | \begin_inset Formula $P$ 309 | \end_inset 310 | 311 | ,矩阵单元格中的元素皆是数字。从矩阵的左上角走到右下角,只能向右或向下走,怎样能使覆盖到的数字之和最大?(TODO:配图?) 312 | \end_layout 313 | 314 | \begin_layout Standard 315 | \begin_inset Formula $M\times N$ 316 | \end_inset 317 | 318 | 的矩阵从左上走到右下,需要走 319 | \begin_inset Formula $M+N-2$ 320 | \end_inset 321 | 322 | 步,其中向下走 323 | \begin_inset Formula $M-1$ 324 | \end_inset 325 | 326 | 步,向右走 327 | \begin_inset Formula $N-1$ 328 | \end_inset 329 | 330 | 步,故可能的方案数共有 331 | \begin_inset Formula $\left(\begin{array}{c} 332 | M-1\\ 333 | M+N-2 334 | \end{array}\right)$ 335 | \end_inset 336 | 337 | 种。用搜索穷举所有方案的时间复杂度太高,考虑使用动态规划算法。 338 | \end_layout 339 | 340 | \begin_layout Standard 341 | 动态规划算法的一个典型思考方式是“只想一步”。以这道题目为例来说明,尽管要求做出的是 342 | \begin_inset Formula $M+N-2$ 343 | \end_inset 344 | 345 | 个向右或者向下的指令,我们不妨想想:知道了什么信息,就可以得出第一步应该怎么走。 346 | \end_layout 347 | 348 | \begin_layout Standard 349 | 第一步有两种走法,向右走从 350 | \begin_inset Formula $(0,0)$ 351 | \end_inset 352 | 353 | 走到 354 | \begin_inset Formula $(0,1)$ 355 | \end_inset 356 | 357 | ,或者向下走从 358 | \begin_inset Formula $(0,0)$ 359 | \end_inset 360 | 361 | 走到 362 | \begin_inset Formula $(1,0)$ 363 | \end_inset 364 | 365 | ,二者的分野不仅存在于这一步覆盖到的数字不同,也体现在今后可选择的策略不同。例如若第一步向右走则左边第一列的数字再也无法拿到。 366 | \end_layout 367 | 368 | \begin_layout Standard 369 | 如何才能判别从 370 | \begin_inset Formula $(0,0)$ 371 | \end_inset 372 | 373 | 到 374 | \begin_inset Formula $(M-1,N-1)$ 375 | \end_inset 376 | 377 | 的路途中第一步怎么走呢?怎样比较第一步的两种走法的优劣呢?很简单,这一方面取决于 378 | \begin_inset Formula $\text{(0,1)}$ 379 | \end_inset 380 | 381 | 和 382 | \begin_inset Formula $\text{(1,0)}$ 383 | \end_inset 384 | 385 | 上面的数字,另一方面也取决于,从这两点出发,走到终点的过程中还会覆盖哪些数字,可能覆盖的最大的数字之和是多少。 386 | \end_layout 387 | 388 | \begin_layout Standard 389 | 于是这样抽象出动态规划的子问题: 390 | \end_layout 391 | 392 | \begin_layout Standard 393 | 设 394 | \begin_inset Formula $F[i,j]$ 395 | \end_inset 396 | 397 | 表示从 398 | \begin_inset Formula $(i,j)$ 399 | \end_inset 400 | 401 | 出发,走到 402 | \begin_inset Formula $(M-1,N-1)$ 403 | \end_inset 404 | 405 | ,可以覆盖到的数字之和的最大值,需求解的原问题既是 406 | \begin_inset Formula $F[0,0]$ 407 | \end_inset 408 | 409 | ,边界条件是 410 | \begin_inset Formula $F[M-1,N-1]=P_{M-1,N-1}$ 411 | \end_inset 412 | 413 | ,状态转移方程是: 414 | \end_layout 415 | 416 | \begin_layout Standard 417 | \begin_inset Formula 418 | \[ 419 | F[i,j]=P_{i,j}+\mathrm{max}\left\{ \begin{array}{c} 420 | F[i,j+1]\:\mathbf{if}\: j+10$ 776 | \end_inset 777 | 778 | )。这个顺序决定了我们把动态规划的状态的表示确定为单元格的坐标时,状态之间的依赖关系是很明晰的,状态的求解顺序也不是个难题。 779 | \end_layout 780 | 781 | \begin_layout Standard 782 | 我们来看一道没有这个只能向右、下走的限制条件的例题 783 | \begin_inset Foot 784 | status open 785 | 786 | \begin_layout Plain Layout 787 | PKU 1088 788 | \end_layout 789 | 790 | \end_inset 791 | 792 | : 793 | \end_layout 794 | 795 | \begin_layout Standard 796 | 仍然是 797 | \begin_inset Formula $M\times N$ 798 | \end_inset 799 | 800 | 的数字矩阵 801 | \begin_inset Formula $P$ 802 | \end_inset 803 | 804 | ,要求寻找的路径可以从任意一点出发,可以向上、下、左、右任意方向延伸,但路径经过的数字必须越来越小、单调递减。求矩阵中存在的最长的满足要求的路径。 805 | \end_layout 806 | 807 | \begin_layout Standard 808 | 去掉了仅能向右、下走及固定起点、终点的限制条件,加上了一个路径中数字单调递减的限制条件。这里还是利用“只想一步”的方法来思考。路径的起点在哪里?不知道,设为 809 | \begin_inset Formula $(i,j)$ 810 | \end_inset 811 | 812 | 。路径的第一步往哪儿走?往哪里走能走得更远就往哪里走。于是状态转移方程是: 813 | \end_layout 814 | 815 | \begin_layout Standard 816 | \begin_inset Formula 817 | \[ 818 | F[i,j]=1+\mathrm{max}\left\{ \begin{array}{c} 819 | 0\\ 820 | F[i,j-1]\text{ if }P_{i,j-1}0$ 1394 | \end_inset 1395 | 1396 | 的最大整数。例如,如果 1397 | \begin_inset Formula $M_{i}$ 1398 | \end_inset 1399 | 1400 | 为 1401 | \begin_inset Formula $13$ 1402 | \end_inset 1403 | 1404 | ,则相应的 1405 | \begin_inset Formula $k=3$ 1406 | \end_inset 1407 | 1408 | ,这种最多取 1409 | \begin_inset Formula $13$ 1410 | \end_inset 1411 | 1412 | 件的物品应被分成系数分别为 1413 | \begin_inset Formula $1,2,4,6$ 1414 | \end_inset 1415 | 1416 | 的四件物品。 1417 | \end_layout 1418 | 1419 | \begin_layout Standard 1420 | 分成的这几件物品的系数和为 1421 | \begin_inset Formula $M_{i}$ 1422 | \end_inset 1423 | 1424 | ,表明不可能取多于 1425 | \begin_inset Formula $M_{i}$ 1426 | \end_inset 1427 | 1428 | 件的第 1429 | \begin_inset Formula $i$ 1430 | \end_inset 1431 | 1432 | 种物品。另外这种方法也能保证对于 1433 | \begin_inset Formula $0\ldots M_{i}$ 1434 | \end_inset 1435 | 1436 | 间的每一个整数,均可以用若干个系数的和表示。这里算法正确性的证明可以分 1437 | \begin_inset Formula $0\ldots2^{k-1}$ 1438 | \end_inset 1439 | 1440 | 和 1441 | \begin_inset Formula $2^{k}\ldots M_{i}$ 1442 | \end_inset 1443 | 1444 | 两段来分别讨论得出,希望读者自己思考尝试一下。 1445 | \end_layout 1446 | 1447 | \begin_layout Standard 1448 | 这样就将第i种物品分成了 1449 | \begin_inset Formula $O(\mathrm{log}M_{i})$ 1450 | \end_inset 1451 | 1452 | 种物品,将原问题转化为了复杂度为 1453 | \begin_inset Formula $O(V\Sigma\mathrm{{log}M_{i})}$ 1454 | \end_inset 1455 | 1456 | 的01背包问题,是很大的改进。 1457 | \end_layout 1458 | 1459 | \begin_layout Standard 1460 | 下面给出 1461 | \begin_inset Formula $O(\mathrm{log}M)$ 1462 | \end_inset 1463 | 1464 | 时间处理一件多重背包中物品的过程: 1465 | \end_layout 1466 | 1467 | \begin_layout LyX-Code 1468 | def 1469 | \begin_inset Formula $\mathsf{MultiplePack}$ 1470 | \end_inset 1471 | 1472 | ( 1473 | \begin_inset Formula $F$ 1474 | \end_inset 1475 | 1476 | , 1477 | \begin_inset Formula $C$ 1478 | \end_inset 1479 | 1480 | , 1481 | \begin_inset Formula $W$ 1482 | \end_inset 1483 | 1484 | , 1485 | \begin_inset Formula $M$ 1486 | \end_inset 1487 | 1488 | ) 1489 | \end_layout 1490 | 1491 | \begin_layout LyX-Code 1492 | if 1493 | \begin_inset Formula $C\cdot M\geq V$ 1494 | \end_inset 1495 | 1496 | 1497 | \end_layout 1498 | 1499 | \begin_layout LyX-Code 1500 | 1501 | \begin_inset Formula $\mathsf{CompletePack}$ 1502 | \end_inset 1503 | 1504 | ( 1505 | \begin_inset Formula $F$ 1506 | \end_inset 1507 | 1508 | , 1509 | \begin_inset Formula $C$ 1510 | \end_inset 1511 | 1512 | , 1513 | \begin_inset Formula $W$ 1514 | \end_inset 1515 | 1516 | ) 1517 | \end_layout 1518 | 1519 | \begin_layout LyX-Code 1520 | return 1521 | \end_layout 1522 | 1523 | \begin_layout LyX-Code 1524 | 1525 | \begin_inset Formula $k\,\leftarrow1$ 1526 | \end_inset 1527 | 1528 | 1529 | \end_layout 1530 | 1531 | \begin_layout LyX-Code 1532 | while 1533 | \begin_inset Formula $k0$ 1750 | \end_inset 1751 | 1752 | 1753 | \end_layout 1754 | 1755 | \begin_layout LyX-Code 1756 | 1757 | \begin_inset Formula $F[i][j+C_{i}]\,\leftarrow\mathrm{max}\{F[i][j+C_{i}],F[i][j]-1\}$ 1758 | \end_inset 1759 | 1760 | 1761 | \end_layout 1762 | 1763 | \begin_layout Standard 1764 | 最终 1765 | \begin_inset Formula $F[N][0\ldots V]$ 1766 | \end_inset 1767 | 1768 | 便是多重背包可行性问题的答案。 1769 | \end_layout 1770 | 1771 | \begin_layout Subsection 1772 | 小结 1773 | \end_layout 1774 | 1775 | \begin_layout Standard 1776 | 在这一讲中,我们看到了将一个算法的复杂度由 1777 | \begin_inset Formula $O(V\Sigma M_{i})$ 1778 | \end_inset 1779 | 1780 | 改进到 1781 | \begin_inset Formula $O(V\Sigma\mathrm{{log}M_{i})}$ 1782 | \end_inset 1783 | 1784 | 的过程,还知道了存在复杂度为 1785 | \begin_inset Formula $O(VN)$ 1786 | \end_inset 1787 | 1788 | 的算法。 1789 | \end_layout 1790 | 1791 | \begin_layout Standard 1792 | 希望你特别注意“拆分物品”的思想和方法,自己证明一下它的正确性,并将完整的程序代码写出来。 1793 | \end_layout 1794 | 1795 | \begin_layout Section 1796 | 混合三种背包问题 1797 | \end_layout 1798 | 1799 | \begin_layout Subsection 1800 | 问题 1801 | \end_layout 1802 | 1803 | \begin_layout Standard 1804 | 如果将前面 1805 | \begin_inset CommandInset ref 1806 | LatexCommand ref 1807 | reference "sec:01背包问题" 1808 | 1809 | \end_inset 1810 | 1811 | 、 1812 | \begin_inset CommandInset ref 1813 | LatexCommand ref 1814 | reference "sec:完全背包问题" 1815 | 1816 | \end_inset 1817 | 1818 | 、 1819 | \begin_inset CommandInset ref 1820 | LatexCommand ref 1821 | reference "sec:多重背包问题" 1822 | 1823 | \end_inset 1824 | 1825 | 中的三种背包问题混合起来。也就是说,有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。应该怎么求解 1826 | 呢? 1827 | \end_layout 1828 | 1829 | \begin_layout Subsection 1830 | 01背包与完全背包的混合 1831 | \end_layout 1832 | 1833 | \begin_layout Standard 1834 | 考虑到01背包和完全背包中给出的伪代码只有一处不同,故如果只有两类物品:一类物品只能取一次,另一类物品可以取无限次,那么只需在对每个物品应用转移方程时,根据物品 1835 | 的类别选用顺序或逆序的循环即可,复杂度是 1836 | \begin_inset Formula $O(VN)$ 1837 | \end_inset 1838 | 1839 | 。伪代码如下: 1840 | \end_layout 1841 | 1842 | \begin_layout LyX-Code 1843 | for 1844 | \begin_inset Formula $i\,\leftarrow1$ 1845 | \end_inset 1846 | 1847 | to 1848 | \begin_inset Formula $N$ 1849 | \end_inset 1850 | 1851 | 1852 | \end_layout 1853 | 1854 | \begin_layout LyX-Code 1855 | if 第 1856 | \begin_inset Formula $i$ 1857 | \end_inset 1858 | 1859 | 件物品属于01背包 1860 | \end_layout 1861 | 1862 | \begin_layout LyX-Code 1863 | for 1864 | \begin_inset Formula $v\,\leftarrow V$ 1865 | \end_inset 1866 | 1867 | to 1868 | \begin_inset Formula $C_{i}$ 1869 | \end_inset 1870 | 1871 | 1872 | \end_layout 1873 | 1874 | \begin_layout LyX-Code 1875 | 1876 | \begin_inset Formula $F[v]\,\leftarrow\mathrm{max}(F[v],F[v-C_{i}]+W_{i})$ 1877 | \end_inset 1878 | 1879 | 1880 | \end_layout 1881 | 1882 | \begin_layout LyX-Code 1883 | else if 第 1884 | \begin_inset Formula $i$ 1885 | \end_inset 1886 | 1887 | 件物品属于完全背包 1888 | \end_layout 1889 | 1890 | \begin_layout LyX-Code 1891 | for 1892 | \begin_inset Formula $v\,\leftarrow C_{i}$ 1893 | \end_inset 1894 | 1895 | to 1896 | \begin_inset Formula $V$ 1897 | \end_inset 1898 | 1899 | 1900 | \end_layout 1901 | 1902 | \begin_layout LyX-Code 1903 | 1904 | \begin_inset Formula $F[v]\,\leftarrow\mathrm{max}(F[v],F[v-C_{i}]+W_{i})$ 1905 | \end_inset 1906 | 1907 | 1908 | \end_layout 1909 | 1910 | \begin_layout Subsection 1911 | 再加上多重背包 1912 | \end_layout 1913 | 1914 | \begin_layout Standard 1915 | 如果再加上最多可以取有限次的多重背包式的物品,那么利用单调队列,也可以给出均摊 1916 | \begin_inset Formula $O(VN)$ 1917 | \end_inset 1918 | 1919 | 的解法。 1920 | \end_layout 1921 | 1922 | \begin_layout Standard 1923 | 但如果不考虑单调队列算法的话,用将每个这类物品分成 1924 | \begin_inset Formula $\text{O(\mathrm{log}}M_{i})$ 1925 | \end_inset 1926 | 1927 | 个01背包的物品的方法也已经很优了。 1928 | \end_layout 1929 | 1930 | \begin_layout Standard 1931 | 最清晰的写法是调用我们前面给出的三个过程。 1932 | \end_layout 1933 | 1934 | \begin_layout LyX-Code 1935 | for 1936 | \begin_inset Formula $i\,\leftarrow1$ 1937 | \end_inset 1938 | 1939 | to 1940 | \begin_inset Formula $N$ 1941 | \end_inset 1942 | 1943 | 1944 | \end_layout 1945 | 1946 | \begin_layout LyX-Code 1947 | if 第 1948 | \begin_inset Formula $i$ 1949 | \end_inset 1950 | 1951 | 件物品属于01背包 1952 | \end_layout 1953 | 1954 | \begin_layout LyX-Code 1955 | 1956 | \begin_inset Formula $\mathsf{ZeroOnePack}$ 1957 | \end_inset 1958 | 1959 | ( 1960 | \begin_inset Formula $F$ 1961 | \end_inset 1962 | 1963 | , 1964 | \begin_inset Formula $C_{i}$ 1965 | \end_inset 1966 | 1967 | , 1968 | \begin_inset Formula $W_{i}$ 1969 | \end_inset 1970 | 1971 | ) 1972 | \end_layout 1973 | 1974 | \begin_layout LyX-Code 1975 | else if 第 1976 | \begin_inset Formula $i$ 1977 | \end_inset 1978 | 1979 | 件物品属于完全背包 1980 | \end_layout 1981 | 1982 | \begin_layout LyX-Code 1983 | 1984 | \begin_inset Formula $\mathsf{CompletePack}$ 1985 | \end_inset 1986 | 1987 | ( 1988 | \begin_inset Formula $F$ 1989 | \end_inset 1990 | 1991 | , 1992 | \begin_inset Formula $C_{i}$ 1993 | \end_inset 1994 | 1995 | , 1996 | \begin_inset Formula $W_{i}$ 1997 | \end_inset 1998 | 1999 | ) 2000 | \end_layout 2001 | 2002 | \begin_layout LyX-Code 2003 | else if 第 2004 | \begin_inset Formula $i$ 2005 | \end_inset 2006 | 2007 | 件物品属于多重背包 2008 | \end_layout 2009 | 2010 | \begin_layout LyX-Code 2011 | 2012 | \begin_inset Formula $\mathsf{MultiplePack}$ 2013 | \end_inset 2014 | 2015 | ( 2016 | \begin_inset Formula $F$ 2017 | \end_inset 2018 | 2019 | , 2020 | \begin_inset Formula $C_{i}$ 2021 | \end_inset 2022 | 2023 | , 2024 | \begin_inset Formula $W_{i}$ 2025 | \end_inset 2026 | 2027 | , 2028 | \begin_inset Formula $N_{i}$ 2029 | \end_inset 2030 | 2031 | ) 2032 | \end_layout 2033 | 2034 | \begin_layout Standard 2035 | 在最初写出这三个过程的时候,可能完全没有想到它们会在这里混合应用。我想这体现了编程中抽象的威力。如果你一直就是以这种“抽象出过程”的方式写每一类背包问题的,也非 2036 | 常清楚它们的实现中细微的不同,那么在遇到混合三种背包问题的题目时,一定能很快想到上面简洁的解法,对吗? 2037 | \end_layout 2038 | 2039 | \begin_layout Subsection 2040 | 小结 2041 | \end_layout 2042 | 2043 | \begin_layout Standard 2044 | 有人说,困难的题目都是由简单的题目叠加而来的。这句话是否公理暂且存之不论,但它在本讲中已经得到了充分的体现。本来01背包、完全背包、多重背包都不是什么难题,但将 2045 | 它们简单地组合起来以后就得到了这样一道一定能吓倒不少人的题目。但只要基础扎实,领会三种基本背包问题的思想,就可以做到把困难的题目拆分成简单的题目来解决。 2046 | \end_layout 2047 | 2048 | \begin_layout Section 2049 | 二维费用的背包问题 2050 | \end_layout 2051 | 2052 | \begin_layout Subsection 2053 | 问题 2054 | \end_layout 2055 | 2056 | \begin_layout Standard 2057 | 二维费用的背包问题是指:对于每件物品,具有两种不同的费用,选择这件物品必须同时付出这两种费用。对于每种费用都有一个可付出的最大值(背包容量)。问怎样选择物品可以 2058 | 得到最大的价值。 2059 | \end_layout 2060 | 2061 | \begin_layout Standard 2062 | 设第 2063 | \begin_inset Formula $i$ 2064 | \end_inset 2065 | 2066 | 件物品所需的两种费用分别为 2067 | \begin_inset Formula $C_{i}$ 2068 | \end_inset 2069 | 2070 | 和 2071 | \begin_inset Formula $D_{i}$ 2072 | \end_inset 2073 | 2074 | 。两种费用可付出的最大值(也即两种背包容量)分别为 2075 | \begin_inset Formula $V$ 2076 | \end_inset 2077 | 2078 | 和 2079 | \begin_inset Formula $U$ 2080 | \end_inset 2081 | 2082 | 。物品的价值为 2083 | \begin_inset Formula $W_{i}$ 2084 | \end_inset 2085 | 2086 | 。 2087 | \end_layout 2088 | 2089 | \begin_layout Subsection 2090 | 算法 2091 | \end_layout 2092 | 2093 | \begin_layout Standard 2094 | 费用加了一维,只需状态也加一维即可。设 2095 | \begin_inset Formula $F[i,v,u]$ 2096 | \end_inset 2097 | 2098 | 表示前 2099 | \begin_inset Formula $i$ 2100 | \end_inset 2101 | 2102 | 件物品付出两种费用分别为 2103 | \begin_inset Formula $v$ 2104 | \end_inset 2105 | 2106 | 和 2107 | \begin_inset Formula $u$ 2108 | \end_inset 2109 | 2110 | 时可获得的最大价值。状态转移方程就是: 2111 | \end_layout 2112 | 2113 | \begin_layout Standard 2114 | \begin_inset Formula 2115 | \[ 2116 | F[i,v,u]=\mathrm{max\{F[i-1,v,u],F[i-1,v-C_{i},u-D_{i}]+W_{i}\}} 2117 | \] 2118 | 2119 | \end_inset 2120 | 2121 | 2122 | \end_layout 2123 | 2124 | \begin_layout Standard 2125 | 如前述优化空间复杂度的方法,可以只使用二维的数组:当每件物品只可以取一次时变量 2126 | \begin_inset Formula $v$ 2127 | \end_inset 2128 | 2129 | 和 2130 | \begin_inset Formula $u$ 2131 | \end_inset 2132 | 2133 | 采用逆序的循环,当物品有如完全背包问题时采用顺序的循环,当物品有如多重背包问题时拆分物品。 2134 | \end_layout 2135 | 2136 | \begin_layout Standard 2137 | 这里就不再给出伪代码了,相信有了前面的基础,读者应该能够自己实现出这个问题的程序。 2138 | \end_layout 2139 | 2140 | \begin_layout Subsection 2141 | 物品总个数的限制 2142 | \end_layout 2143 | 2144 | \begin_layout Standard 2145 | 有时,“二维费用”的条件是以这样一种隐含的方式给出的:最多只能取 2146 | \begin_inset Formula $U$ 2147 | \end_inset 2148 | 2149 | 件物品。这事实上相当于每件物品多了一种“件数”的费用,每个物品的件数费用均为 2150 | \begin_inset Formula $1$ 2151 | \end_inset 2152 | 2153 | ,可以付出的最大件数费用为 2154 | \begin_inset Formula $U$ 2155 | \end_inset 2156 | 2157 | 。换句话说,设 2158 | \begin_inset Formula $F[v,u]$ 2159 | \end_inset 2160 | 2161 | 表示付出费用 2162 | \begin_inset Formula $v$ 2163 | \end_inset 2164 | 2165 | 、最多选 2166 | \begin_inset Formula $u$ 2167 | \end_inset 2168 | 2169 | 件时可得到的最大价值,则根据物品的类型(01、完全、多重)用不同的方法循环更新,最后在 2170 | \begin_inset Formula $f[0\ldots V,0\ldots U]$ 2171 | \end_inset 2172 | 2173 | 范围内寻找答案。 2174 | \end_layout 2175 | 2176 | \begin_layout Subsection 2177 | 二维整数域 2178 | \begin_inset Formula $N^{2}$ 2179 | \end_inset 2180 | 2181 | 上的背包问题 2182 | \end_layout 2183 | 2184 | \begin_layout Standard 2185 | 另一种看待二维背包问题的思路是:将它看待成 2186 | \begin_inset Formula $N^{2}$ 2187 | \end_inset 2188 | 2189 | 域上的背包问题。也就是说,背包的容量以及每件物品的费用都是一个二维向量。而常见的一维背包问题则是自然数域上的背包问题。所以说,一维背包的种种思想方法,往往可以应 2190 | 用于二位背包问题的求解中,因为只是数域扩大了而已。 2191 | \end_layout 2192 | 2193 | \begin_layout Standard 2194 | 作为这种思想的练习,你可以尝试将后文中提到的“子集和问题”扩展到二维,并试图用同样的复杂度解决。 2195 | \end_layout 2196 | 2197 | \begin_layout Subsection 2198 | 小结 2199 | \end_layout 2200 | 2201 | \begin_layout Standard 2202 | 当发现由熟悉的动态规划题目变形得来的题目时,在原来的状态中加一维以满足新的限制是一种比较通用的方法。希望你能从本讲中初步体会到这种方法。 2203 | \end_layout 2204 | 2205 | \begin_layout Section 2206 | \begin_inset CommandInset label 2207 | LatexCommand label 2208 | name "sec:分组的背包问题" 2209 | 2210 | \end_inset 2211 | 2212 | 分组的背包问题 2213 | \end_layout 2214 | 2215 | \begin_layout Subsection 2216 | 问题 2217 | \end_layout 2218 | 2219 | \begin_layout Standard 2220 | 有 2221 | \begin_inset Formula $N$ 2222 | \end_inset 2223 | 2224 | 件物品和一个容量为 2225 | \begin_inset Formula $V$ 2226 | \end_inset 2227 | 2228 | 的背包。第 2229 | \begin_inset Formula $i$ 2230 | \end_inset 2231 | 2232 | 件物品的费用是 2233 | \begin_inset Formula $C_{i}$ 2234 | \end_inset 2235 | 2236 | ,价值是 2237 | \begin_inset Formula $W_{i}$ 2238 | \end_inset 2239 | 2240 | 。这些物品被划分为 2241 | \begin_inset Formula $K$ 2242 | \end_inset 2243 | 2244 | 组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。 2245 | \end_layout 2246 | 2247 | \begin_layout Subsection 2248 | 算法 2249 | \end_layout 2250 | 2251 | \begin_layout Standard 2252 | 这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设 2253 | \begin_inset Formula $F[k,v]$ 2254 | \end_inset 2255 | 2256 | 表示前 2257 | \begin_inset Formula $k$ 2258 | \end_inset 2259 | 2260 | 组物品花费费用 2261 | \begin_inset Formula $v$ 2262 | \end_inset 2263 | 2264 | 能取得的最大权值,则有: 2265 | \end_layout 2266 | 2267 | \begin_layout Standard 2268 | \begin_inset Formula 2269 | \[ 2270 | F[k,v]=\mathrm{max}\{F[k-1,v],F[k-1,v-C_{i}]+W_{i}\:|\:\mathrm{item}\: i\in\mathrm{group}\: k\} 2271 | \] 2272 | 2273 | \end_inset 2274 | 2275 | 2276 | \end_layout 2277 | 2278 | \begin_layout Standard 2279 | 使用一维数组的伪代码如下: 2280 | \end_layout 2281 | 2282 | \begin_layout LyX-Code 2283 | for 2284 | \begin_inset Formula $k\,\leftarrow1$ 2285 | \end_inset 2286 | 2287 | to 2288 | \begin_inset Formula $K$ 2289 | \end_inset 2290 | 2291 | 2292 | \end_layout 2293 | 2294 | \begin_layout LyX-Code 2295 | for 2296 | \begin_inset Formula $v\,\leftarrow V$ 2297 | \end_inset 2298 | 2299 | to 2300 | \begin_inset Formula $0$ 2301 | \end_inset 2302 | 2303 | 2304 | \end_layout 2305 | 2306 | \begin_layout LyX-Code 2307 | for all item 2308 | \begin_inset Formula $i$ 2309 | \end_inset 2310 | 2311 | in group 2312 | \begin_inset Formula $k$ 2313 | \end_inset 2314 | 2315 | 2316 | \end_layout 2317 | 2318 | \begin_layout LyX-Code 2319 | 2320 | \begin_inset Formula $F[v]\,\leftarrow\mathrm{max}\{F[v],F[v-C_{i}]+W_{i}\}$ 2321 | \end_inset 2322 | 2323 | 2324 | \end_layout 2325 | 2326 | \begin_layout Standard 2327 | 这里三层循环的顺序保证了每一组内的物品最多只有一个会被添加到背包中。 2328 | \end_layout 2329 | 2330 | \begin_layout Standard 2331 | 另外,显然可以对每组内的物品应用 2332 | \begin_inset CommandInset ref 2333 | LatexCommand ref 2334 | reference "sub:一个简单有效的优化" 2335 | 2336 | \end_inset 2337 | 2338 | 中的优化。 2339 | \end_layout 2340 | 2341 | \begin_layout Subsection 2342 | 小结 2343 | \end_layout 2344 | 2345 | \begin_layout Standard 2346 | 分组的背包问题将彼此互斥的若干物品称为一个组,这建立了一个很好的模型。不少背包问题的变形都可以转化为分组的背包问题(例如 2347 | \begin_inset CommandInset ref 2348 | LatexCommand ref 2349 | reference "sec:有依赖的背包问题" 2350 | 2351 | \end_inset 2352 | 2353 | ),由分组的背包问题进一步可定义“泛化物品”的概念,十分有利于解题。 2354 | \end_layout 2355 | 2356 | \begin_layout Section 2357 | \begin_inset CommandInset label 2358 | LatexCommand label 2359 | name "sec:有依赖的背包问题" 2360 | 2361 | \end_inset 2362 | 2363 | 有依赖的背包问题 2364 | \end_layout 2365 | 2366 | \begin_layout Subsection 2367 | 简化的问题 2368 | \end_layout 2369 | 2370 | \begin_layout Standard 2371 | 这种背包问题的物品间存在某种“依赖”的关系。也就是说,物品 2372 | \begin_inset Formula $i$ 2373 | \end_inset 2374 | 2375 | 依赖于物品 2376 | \begin_inset Formula $j$ 2377 | \end_inset 2378 | 2379 | ,表示若选物品 2380 | \begin_inset Formula $i$ 2381 | \end_inset 2382 | 2383 | ,则必须选物品 2384 | \begin_inset Formula $j$ 2385 | \end_inset 2386 | 2387 | 。为了简化起见,我们先设没有某个物品既依赖于别的物品,又被别的物品所依赖;另外,没有某件物品同时依赖多件物品。 2388 | \end_layout 2389 | 2390 | \begin_layout Subsection 2391 | 算法 2392 | \end_layout 2393 | 2394 | \begin_layout Standard 2395 | 这个问题由NOIP2006中“金明的预算方案”一题扩展而来。遵从该题的提法,将不依赖于别的物品的物品称为“主件”,依赖于某主件的物品称为“附件”。由这个问题的简 2396 | 化条件可知所有的物品由若干主件和依赖于每个主件的一个附件集合组成。 2397 | \end_layout 2398 | 2399 | \begin_layout Standard 2400 | 按照背包问题的一般思路,仅考虑一个主件和它的附件集合。可是,可用的策略非常多,包括:一个也不选,仅选择主件,选择主件后再选择一个附件,选择主件后再选择两个附件… 2401 | …无法用状态转移方程来表示如此多的策略。事实上,设有 2402 | \begin_inset Formula $n$ 2403 | \end_inset 2404 | 2405 | 个附件,则策略有 2406 | \begin_inset Formula $2^{n}+1$ 2407 | \end_inset 2408 | 2409 | 个,为指数级。 2410 | \end_layout 2411 | 2412 | \begin_layout Standard 2413 | 考虑到所有这些策略都是互斥的(也就是说,你只能选择一种策略),所以一个主件和它的附件集合实际上对应于 2414 | \begin_inset CommandInset ref 2415 | LatexCommand ref 2416 | reference "sec:分组的背包问题" 2417 | 2418 | \end_inset 2419 | 2420 | 中的一个物品组,每个选择了主件又选择了若干个附件的策略对应于这个物品组中的一个物品,其费用和价值都是这个策略中的物品的值的和。但仅仅是这一步转化并不能给出一个好 2421 | 的算法,因为物品组中的物品还是像原问题的策略一样多。 2422 | \end_layout 2423 | 2424 | \begin_layout Standard 2425 | 再考虑对每组内的物品应用 2426 | \begin_inset CommandInset ref 2427 | LatexCommand ref 2428 | reference "sub:一个简单有效的优化" 2429 | 2430 | \end_inset 2431 | 2432 | 中的优化。我们可以想到,对于第 2433 | \begin_inset Formula $k$ 2434 | \end_inset 2435 | 2436 | 个物品组中的物品,所有费用相同的物品只留一个价值最大的,不影响结果。所以,可以对主件 2437 | \begin_inset Formula $k$ 2438 | \end_inset 2439 | 2440 | 的“附件集合”先进行一次01背包,得到费用依次为 2441 | \begin_inset Formula $0\text{\ldots}V-C_{k}$ 2442 | \end_inset 2443 | 2444 | 所有这些值时相应的最大价值 2445 | \begin_inset Formula $F_{k}[0\ldots V-C_{k}]$ 2446 | \end_inset 2447 | 2448 | 。那么,这个主件及它的附件集合相当于 2449 | \begin_inset Formula $V-C_{k}+1$ 2450 | \end_inset 2451 | 2452 | 个物品的物品组,其中费用为 2453 | \begin_inset Formula $v$ 2454 | \end_inset 2455 | 2456 | 的物品的价值为 2457 | \begin_inset Formula $F_{k}[v-C_{k}]+W_{k}$ 2458 | \end_inset 2459 | 2460 | , 2461 | \begin_inset Formula $v$ 2462 | \end_inset 2463 | 2464 | 的取值范围是 2465 | \begin_inset Formula $C_{k}\leq v\leq V$ 2466 | \end_inset 2467 | 2468 | 。 2469 | \end_layout 2470 | 2471 | \begin_layout Standard 2472 | 也就是说,原来指数级的策略中,有很多策略都是冗余的,通过一次01背包后,将主件 2473 | \begin_inset Formula $k$ 2474 | \end_inset 2475 | 2476 | 及其附件转化为 2477 | \begin_inset Formula $V-C_{k}+1$ 2478 | \end_inset 2479 | 2480 | 个物品的物品组,就可以直接应用 2481 | \begin_inset CommandInset ref 2482 | LatexCommand ref 2483 | reference "sec:分组的背包问题" 2484 | 2485 | \end_inset 2486 | 2487 | 的算法解决问题了。 2488 | \end_layout 2489 | 2490 | \begin_layout Subsection 2491 | 较一般的问题 2492 | \end_layout 2493 | 2494 | \begin_layout Standard 2495 | 更一般的问题是:依赖关系以图论中“森林” 2496 | \begin_inset Foot 2497 | status open 2498 | 2499 | \begin_layout Plain Layout 2500 | 即多叉树的集合 2501 | \end_layout 2502 | 2503 | \end_inset 2504 | 2505 | 的形式给出。也就是说,主件的附件仍然可以具有自己的附件集合。限制只是每个物品最多只依赖于一个物品(只有一个主件)且不出现循环依赖。 2506 | \end_layout 2507 | 2508 | \begin_layout Standard 2509 | 解决这个问题仍然可以用将每个主件及其附件集合转化为物品组的方式。唯一不同的是,由于附件可能还有附件,就不能将每个附件都看作一个一般的01背包中的物品了。若这个附 2510 | 件也有附件集合,则它必定要被先转化为物品组,然后用分组的背包问题解出主件及其附件集合所对应的附件组中各个费用的附件所对应的价值。 2511 | \end_layout 2512 | 2513 | \begin_layout Standard 2514 | 事实上,这是一种树形动态规划,其特点是,在用动态规划求每个父节点的属性之前,需要对它的各个儿子的属性进行一次动态规划式的求值。这已经触及到了“泛化物品”的思想。 2515 | 看完 2516 | \begin_inset CommandInset ref 2517 | LatexCommand ref 2518 | reference "sec:泛化物品" 2519 | 2520 | \end_inset 2521 | 2522 | 后,你会发现这个“依赖关系树”每一个子树都等价于一件泛化物品,求某节点为根的子树对应的泛化物品相当于求其所有儿子的对应的泛化物品之和。 2523 | \end_layout 2524 | 2525 | \begin_layout Subsection 2526 | 小结 2527 | \end_layout 2528 | 2529 | \begin_layout Standard 2530 | NOIP2006的那道背包问题我做得很失败,写了上百行的代码,却一分未得。后来我通过思考发现通过引入“物品组”和“依赖”的概念可以加深对这题的理解,还可以解决它 2531 | 的推广问题。用物品组的思想考虑那题中极其特殊的依赖关系:物品不能既作主件又作附件,每个主件最多有两个附件,可以发现一个主件和它的两个附件等价于一个由四个物品组成 2532 | 的物品组,这便揭示了问题的某种本质。 2533 | \end_layout 2534 | 2535 | \begin_layout Standard 2536 | 后来,我在《背包问题九讲》第一版中总结此事时说:“失败不是什么丢人的事情,从失败中全无收获才是。”之后的NOIP2007的比赛中,我得了满分。 2537 | \end_layout 2538 | 2539 | \begin_layout Section 2540 | \begin_inset CommandInset label 2541 | LatexCommand label 2542 | name "sec:泛化物品" 2543 | 2544 | \end_inset 2545 | 2546 | 泛化物品 2547 | \end_layout 2548 | 2549 | \begin_layout Subsection 2550 | 定义 2551 | \end_layout 2552 | 2553 | \begin_layout Standard 2554 | 考虑这样一种物品,它并没有固定的费用和价值,而是它的价值随着你分配给它的费用而变化。这就是泛化物品的概念。 2555 | \end_layout 2556 | 2557 | \begin_layout Standard 2558 | 更严格的定义之。在背包容量为 2559 | \begin_inset Formula $V$ 2560 | \end_inset 2561 | 2562 | 的背包问题中,泛化物品是一个定义域为 2563 | \begin_inset Formula $0\ldots V$ 2564 | \end_inset 2565 | 2566 | 中的整数的函数 2567 | \begin_inset Formula $h$ 2568 | \end_inset 2569 | 2570 | ,当分配给它的费用为 2571 | \begin_inset Formula $v$ 2572 | \end_inset 2573 | 2574 | 时,能得到的价值就是 2575 | \begin_inset Formula $h(v)$ 2576 | \end_inset 2577 | 2578 | 。 2579 | \end_layout 2580 | 2581 | \begin_layout Standard 2582 | 这个定义有一点点抽象,另一种理解是一个泛化物品就是一个数组 2583 | \begin_inset Formula $h[0\ldots V]$ 2584 | \end_inset 2585 | 2586 | ,给它费用 2587 | \begin_inset Formula $v$ 2588 | \end_inset 2589 | 2590 | ,可得到价值 2591 | \begin_inset Formula $h[v]$ 2592 | \end_inset 2593 | 2594 | 。 2595 | \end_layout 2596 | 2597 | \begin_layout Standard 2598 | 一个费用为 2599 | \begin_inset Formula $c$ 2600 | \end_inset 2601 | 2602 | 价值为 2603 | \begin_inset Formula $w$ 2604 | \end_inset 2605 | 2606 | 的物品,如果它是01背包中的物品,那么把它看成泛化物品,它就是除了 2607 | \begin_inset Formula $h(c)=w$ 2608 | \end_inset 2609 | 2610 | 外,其它函数值都为 2611 | \begin_inset Formula $0$ 2612 | \end_inset 2613 | 2614 | 的一个函数。如果它是完全背包中的物品,那么它可以看成这样一个函数,仅当 2615 | \begin_inset Formula $v$ 2616 | \end_inset 2617 | 2618 | 被 2619 | \begin_inset Formula $c$ 2620 | \end_inset 2621 | 2622 | 整除时有 2623 | \begin_inset Formula $h(v)=w\cdot\frac{v}{c}$ 2624 | \end_inset 2625 | 2626 | ,其它函数值均为 2627 | \begin_inset Formula $0$ 2628 | \end_inset 2629 | 2630 | 。如果它是多重背包中重复次数最多为 2631 | \begin_inset Formula $m$ 2632 | \end_inset 2633 | 2634 | 的物品,那么它对应的泛化物品的函数有 2635 | \begin_inset Formula $h(v)=w\cdot\frac{v}{c}$ 2636 | \end_inset 2637 | 2638 | 仅当 2639 | \begin_inset Formula $v$ 2640 | \end_inset 2641 | 2642 | 被 2643 | \begin_inset Formula $c$ 2644 | \end_inset 2645 | 2646 | 整除且 2647 | \begin_inset Formula $\frac{v}{c}\leq m$ 2648 | \end_inset 2649 | 2650 | ,其它情况函数值均为 2651 | \begin_inset Formula $0$ 2652 | \end_inset 2653 | 2654 | 。 2655 | \end_layout 2656 | 2657 | \begin_layout Standard 2658 | 一个物品组可以看作一个泛化物品 2659 | \begin_inset Formula $h$ 2660 | \end_inset 2661 | 2662 | 。对于一个 2663 | \begin_inset Formula $0\ldots V$ 2664 | \end_inset 2665 | 2666 | 中的 2667 | \begin_inset Formula $v$ 2668 | \end_inset 2669 | 2670 | ,若物品组中不存在费用为 2671 | \begin_inset Formula $v$ 2672 | \end_inset 2673 | 2674 | 的物品,则 2675 | \begin_inset Formula $h(v)=0$ 2676 | \end_inset 2677 | 2678 | ,否则 2679 | \begin_inset Formula $h(v)$ 2680 | \end_inset 2681 | 2682 | 取值为所有费用为 2683 | \begin_inset Formula $v$ 2684 | \end_inset 2685 | 2686 | 的物品的最大价值。 2687 | \begin_inset CommandInset ref 2688 | LatexCommand ref 2689 | reference "sec:分组的背包问题" 2690 | 2691 | \end_inset 2692 | 2693 | 中每个主件及其附件集合等价于一个物品组,自然也可看作一个泛化物品。 2694 | \end_layout 2695 | 2696 | \begin_layout Subsection 2697 | 泛化物品的和 2698 | \end_layout 2699 | 2700 | \begin_layout Standard 2701 | 如果给定了两个泛化物品 2702 | \begin_inset Formula $h$ 2703 | \end_inset 2704 | 2705 | 和 2706 | \begin_inset Formula $l$ 2707 | \end_inset 2708 | 2709 | ,要用一定的费用从这两个泛化物品中得到最大的价值,这个问题怎么求呢?事实上,对于一个给定的费用 2710 | \begin_inset Formula $v$ 2711 | \end_inset 2712 | 2713 | ,只需枚举将这个费用如何分配给两个泛化物品就可以了。同样的,对于 2714 | \begin_inset Formula $0\text{\ldots}V$ 2715 | \end_inset 2716 | 2717 | 中的每一个整数 2718 | \begin_inset Formula $v$ 2719 | \end_inset 2720 | 2721 | ,可以求得费用 2722 | \begin_inset Formula $v$ 2723 | \end_inset 2724 | 2725 | 分配到 2726 | \begin_inset Formula $h$ 2727 | \end_inset 2728 | 2729 | 和 2730 | \begin_inset Formula $l$ 2731 | \end_inset 2732 | 2733 | 中的最大价值 2734 | \begin_inset Formula $f(v)$ 2735 | \end_inset 2736 | 2737 | 。也即 2738 | \end_layout 2739 | 2740 | \begin_layout Standard 2741 | \begin_inset Formula 2742 | \[ 2743 | f(v)=\mathrm{max}\{h(k)+l(v-k)\:|\:0\leq k\leq v\} 2744 | \] 2745 | 2746 | \end_inset 2747 | 2748 | 2749 | \end_layout 2750 | 2751 | \begin_layout Standard 2752 | 可以看到,这里的 2753 | \begin_inset Formula $f$ 2754 | \end_inset 2755 | 2756 | 是一个由泛化物品 2757 | \begin_inset Formula $h$ 2758 | \end_inset 2759 | 2760 | 和 2761 | \begin_inset Formula $l$ 2762 | \end_inset 2763 | 2764 | 决定的定义域为 2765 | \begin_inset Formula $0\text{\ldots V}$ 2766 | \end_inset 2767 | 2768 | 的函数,也就是说, 2769 | \begin_inset Formula $f$ 2770 | \end_inset 2771 | 2772 | 是一个由泛化物品 2773 | \begin_inset Formula $h$ 2774 | \end_inset 2775 | 2776 | 和 2777 | \begin_inset Formula $l$ 2778 | \end_inset 2779 | 2780 | 决定的泛化物品。 2781 | \end_layout 2782 | 2783 | \begin_layout Standard 2784 | 我们将 2785 | \begin_inset Formula $f$ 2786 | \end_inset 2787 | 2788 | 定义为泛化物品 2789 | \begin_inset Formula $h$ 2790 | \end_inset 2791 | 2792 | 和 2793 | \begin_inset Formula $l$ 2794 | \end_inset 2795 | 2796 | 的和: 2797 | \begin_inset Formula $h$ 2798 | \end_inset 2799 | 2800 | 、 2801 | \begin_inset Formula $l$ 2802 | \end_inset 2803 | 2804 | 都是泛化物品,若函数 2805 | \begin_inset Formula $f$ 2806 | \end_inset 2807 | 2808 | 满足以上关系式,则称 2809 | \begin_inset Formula $f$ 2810 | \end_inset 2811 | 2812 | 是 2813 | \begin_inset Formula $h$ 2814 | \end_inset 2815 | 2816 | 与 2817 | \begin_inset Formula $l$ 2818 | \end_inset 2819 | 2820 | 的和。泛化物品和运算的时间复杂度取决于背包的容量,是 2821 | \begin_inset Formula $O(V^{2})$ 2822 | \end_inset 2823 | 2824 | 。 2825 | \end_layout 2826 | 2827 | \begin_layout Standard 2828 | 由泛化物品的定义可知:在一个背包问题中,若将两个泛化物品代以它们的和,不影响问题的答案。事实上,对于其中的物品都是泛化物品的背包问题,求它的答案的过程也就是求所 2829 | 有这些泛化物品之和的过程。若问题的和为 2830 | \begin_inset Formula $s$ 2831 | \end_inset 2832 | 2833 | ,则答案就是 2834 | \begin_inset Formula $s\text{(}0\ldots V)$ 2835 | \end_inset 2836 | 2837 | 中的最大值。 2838 | \end_layout 2839 | 2840 | \begin_layout Subsection 2841 | 背包问题的泛化物品 2842 | \end_layout 2843 | 2844 | \begin_layout Standard 2845 | 一个背包问题中,可能会给出很多条件,包括每种物品的费用、价值等属性,物品之间的分组、依赖等关系等。但肯定能将问题对应于某个泛化物品。也就是说,给定了所有条件以后 2846 | ,就可以对每个非负整数 2847 | \begin_inset Formula $v$ 2848 | \end_inset 2849 | 2850 | 求得:若背包容量为 2851 | \begin_inset Formula $v$ 2852 | \end_inset 2853 | 2854 | ,将物品装入背包可得到的最大价值是多少,这可以认为是定义在非负整数集上的一件泛化物品。这个泛化物品——或者说问题所对应的一个定义域为非负整数的函数——包含了关于 2855 | 问题本身的高度浓缩的信息。一般而言,求得这个泛化物品的一个子定义域(例如 2856 | \begin_inset Formula $0\text{\ldots}V$ 2857 | \end_inset 2858 | 2859 | )的值之后,就可以根据这个函数的取值得到背包问题的最终答案。 2860 | \end_layout 2861 | 2862 | \begin_layout Standard 2863 | 综上所述,一般而言,求解背包问题,即求解这个问题所对应的一个函数,即该问题的泛化物品。而求解某个泛化物品的一种常用方法就是将它表示为若干泛化物品的和然后求之。 2864 | \end_layout 2865 | 2866 | \begin_layout Subsection 2867 | 小结 2868 | \end_layout 2869 | 2870 | \begin_layout Standard 2871 | 本讲是我在学习函数式编程的Scheme语言时,用函数编程的眼光审视各类背包问题得出的理论。 2872 | \end_layout 2873 | 2874 | \begin_layout Standard 2875 | 我想说:“思考”是一个程序员最重要的品质。简单的问题,深入思考以后,也能发现更多。 2876 | \end_layout 2877 | 2878 | \begin_layout Section 2879 | 背包问题问法的变化 2880 | \end_layout 2881 | 2882 | \begin_layout Standard 2883 | 以上涉及的各种背包问题都是要求在背包容量(费用)的限制下求可以取到的最大价值,但背包问题还有很多种灵活的问法,在这里值得提一下。但是我认为,只要深入理解了求背包 2884 | 问题最大价值的方法,即使问法变化了,也是不难想出算法的。 2885 | \end_layout 2886 | 2887 | \begin_layout Standard 2888 | 例如,求解最多可以放多少件物品或者最多可以装满多少背包的空间。这都可以根据具体问题利用前面的方程求出所有状态的值( 2889 | \begin_inset Formula $F$ 2890 | \end_inset 2891 | 2892 | 数组)之后得到。 2893 | \end_layout 2894 | 2895 | \begin_layout Standard 2896 | 还有,如果要求的是“总价值最小”“总件数最小”,只需将状态转移方程中的 2897 | \begin_inset Formula $\mathrm{max}$ 2898 | \end_inset 2899 | 2900 | 改成 2901 | \begin_inset Formula $\mathrm{min}$ 2902 | \end_inset 2903 | 2904 | 即可。 2905 | \end_layout 2906 | 2907 | \begin_layout Standard 2908 | 下面说一些变化更大的问法。 2909 | \end_layout 2910 | 2911 | \begin_layout Subsection 2912 | 输出方案 2913 | \end_layout 2914 | 2915 | \begin_layout Standard 2916 | 一般而言,背包问题是要求一个最优值,如果要求输出这个最优值的方案,可以参照一般动态规划问题输出方案的方法:记录下每个状态的最优值是由状态转移方程的哪一项推出来的 2917 | ,换句话说,记录下它是由哪一个策略推出来的。便可根据这条策略找到上一个状态,从上一个状态接着向前推即可。 2918 | \end_layout 2919 | 2920 | \begin_layout Standard 2921 | 还是以01背包为例,方程为 2922 | \begin_inset Formula $F[i,v]=\mathrm{max}\{F[i-1,v],F[i-1,v-C_{i}]+W_{i}\}$ 2923 | \end_inset 2924 | 2925 | 。再用一个数组 2926 | \begin_inset Formula $G[i,v]$ 2927 | \end_inset 2928 | 2929 | ,设 2930 | \begin_inset Formula $G[i,v]=0$ 2931 | \end_inset 2932 | 2933 | 表示推出 2934 | \begin_inset Formula $F[i,v]$ 2935 | \end_inset 2936 | 2937 | 的值时是采用了方程的前一项(也即 2938 | \begin_inset Formula $F[i,v]=F[i-1,v]$ 2939 | \end_inset 2940 | 2941 | ), 2942 | \begin_inset Formula $G[i,v]=1$ 2943 | \end_inset 2944 | 2945 | 表示采用了方程的后一项。注意这两项分别表示了两种策略:未选第 2946 | \begin_inset Formula $i$ 2947 | \end_inset 2948 | 2949 | 个物品及选了第 2950 | \begin_inset Formula $i$ 2951 | \end_inset 2952 | 2953 | 个物品。那么输出方案的伪代码可以这样写(设最终状态为 2954 | \begin_inset Formula $F[N,V]$ 2955 | \end_inset 2956 | 2957 | ): 2958 | \end_layout 2959 | 2960 | \begin_layout LyX-Code 2961 | \begin_inset Formula $i\text{\,\leftarrow}N$ 2962 | \end_inset 2963 | 2964 | 2965 | \end_layout 2966 | 2967 | \begin_layout LyX-Code 2968 | \begin_inset Formula $v\,\leftarrow V$ 2969 | \end_inset 2970 | 2971 | 2972 | \end_layout 2973 | 2974 | \begin_layout LyX-Code 2975 | while 2976 | \begin_inset Formula $i>0$ 2977 | \end_inset 2978 | 2979 | 2980 | \end_layout 2981 | 2982 | \begin_layout LyX-Code 2983 | if 2984 | \begin_inset Formula $G[i,v]=0$ 2985 | \end_inset 2986 | 2987 | 2988 | \end_layout 2989 | 2990 | \begin_layout LyX-Code 2991 | print 未选第 2992 | \begin_inset Formula $i$ 2993 | \end_inset 2994 | 2995 | 项物品 2996 | \end_layout 2997 | 2998 | \begin_layout LyX-Code 2999 | else if 3000 | \begin_inset Formula $G[i,v]=1$ 3001 | \end_inset 3002 | 3003 | 3004 | \end_layout 3005 | 3006 | \begin_layout LyX-Code 3007 | print 3008 | \begin_inset Formula $ $ 3009 | \end_inset 3010 | 3011 | 选了第 3012 | \begin_inset Formula $i$ 3013 | \end_inset 3014 | 3015 | 项物品 3016 | \end_layout 3017 | 3018 | \begin_layout LyX-Code 3019 | 3020 | \begin_inset Formula $v\,\leftarrow sv-C_{i}$ 3021 | \end_inset 3022 | 3023 | 3024 | \end_layout 3025 | 3026 | \begin_layout LyX-Code 3027 | 3028 | \begin_inset Formula $i\,\leftarrow i-1$ 3029 | \end_inset 3030 | 3031 | 3032 | \end_layout 3033 | 3034 | \begin_layout Standard 3035 | 另外,采用方程的前一项或后一项也可以在输出方案的过程中根据 3036 | \begin_inset Formula $F[i,v]$ 3037 | \end_inset 3038 | 3039 | 的值实时地求出来。也即,不须纪录 3040 | \begin_inset Formula $G$ 3041 | \end_inset 3042 | 3043 | 数组,将上述代码中的 3044 | \begin_inset Formula $G[i,v]=0$ 3045 | \end_inset 3046 | 3047 | 改成 3048 | \begin_inset Formula $F[i,v]=F[i-1,v]$ 3049 | \end_inset 3050 | 3051 | , 3052 | \begin_inset Formula $G[i,v]=1$ 3053 | \end_inset 3054 | 3055 | 改成 3056 | \begin_inset Formula $F[i,v]=F[i-1][v-C_{i}]+W_{i}$ 3057 | \end_inset 3058 | 3059 | 也可。 3060 | \end_layout 3061 | 3062 | \begin_layout Subsection 3063 | 输出字典序最小的最优方案 3064 | \end_layout 3065 | 3066 | \begin_layout Standard 3067 | 这里“字典序最小”的意思是 3068 | \begin_inset Formula $1\ldots N$ 3069 | \end_inset 3070 | 3071 | 号物品的选择方案排列出来以后字典序最小。以输出01背包最小字典序的方案为例。 3072 | \end_layout 3073 | 3074 | \begin_layout Standard 3075 | 一般而言,求一个字典序最小的最优方案,只需要在转移时注意策略。 3076 | \end_layout 3077 | 3078 | \begin_layout Standard 3079 | 首先,子问题的定义要略改一些。我们注意到,如果存在一个选了物品 3080 | \begin_inset Formula $1$ 3081 | \end_inset 3082 | 3083 | 的最优方案,那么答案一定包含物品 3084 | \begin_inset Formula $1$ 3085 | \end_inset 3086 | 3087 | ,原问题转化为一个背包容量为 3088 | \begin_inset Formula $V-C_{1}$ 3089 | \end_inset 3090 | 3091 | ,物品为 3092 | \begin_inset Formula $2\ldots N$ 3093 | \end_inset 3094 | 3095 | 的子问题。反之,如果答案不包含物品 3096 | \begin_inset Formula $1$ 3097 | \end_inset 3098 | 3099 | ,则转化成背包容量仍为 3100 | \begin_inset Formula $V$ 3101 | \end_inset 3102 | 3103 | ,物品为 3104 | \begin_inset Formula $2\ldots N$ 3105 | \end_inset 3106 | 3107 | 的子问题。 3108 | \end_layout 3109 | 3110 | \begin_layout Standard 3111 | 不管答案怎样,子问题的物品都是以 3112 | \begin_inset Formula $i\ldots N$ 3113 | \end_inset 3114 | 3115 | 而非前所述的 3116 | \begin_inset Formula $1\ldots i$ 3117 | \end_inset 3118 | 3119 | 的形式来定义的,所以状态的定义和转移方程都需要改一下。 3120 | \end_layout 3121 | 3122 | \begin_layout Standard 3123 | 但也许更简易的方法是,先把物品编号做 3124 | \begin_inset Formula $x\,\leftarrow N+1-x$ 3125 | \end_inset 3126 | 3127 | 的变换,在输出方案时再变换回来。在做完物品编号的变换后,可以按照前面经典的转移方程来求值。只是在输出方案时要注意,如果 3128 | \begin_inset Formula $F[i,v]=F[i-1,v]$ 3129 | \end_inset 3130 | 3131 | 和 3132 | \begin_inset Formula $F[i,v]=F[i-1][v-C_{i}]+W_{i}$ 3133 | \end_inset 3134 | 3135 | 都成立,应该按照后者来输出方案,即选择了物品 3136 | \begin_inset Formula $i$ 3137 | \end_inset 3138 | 3139 | ,输出其原来的编号 3140 | \begin_inset Formula $N-1-i$ 3141 | \end_inset 3142 | 3143 | 。 3144 | \end_layout 3145 | 3146 | \begin_layout Subsection 3147 | 求方案总数 3148 | \end_layout 3149 | 3150 | \begin_layout Standard 3151 | 对于一个给定了背包容量、物品费用、物品间相互关系(分组、依赖等)的背包问题,除了再给定每个物品的价值后求可得到的最大价值外,还可以得到装满背包或将背包装至某一指 3152 | 定容量的方案总数。 3153 | \end_layout 3154 | 3155 | \begin_layout Standard 3156 | 对于这类改变问法的问题,一般只需将状态转移方程中的 3157 | \begin_inset Formula $\mathrm{max}$ 3158 | \end_inset 3159 | 3160 | 改成 3161 | \begin_inset Formula $\mathrm{sum}$ 3162 | \end_inset 3163 | 3164 | 即可。例如若每件物品均是完全背包中的物品,转移方程即为 3165 | \end_layout 3166 | 3167 | \begin_layout Standard 3168 | \begin_inset Formula 3169 | \[ 3170 | F[i,v]=\mathrm{sum}\{{F[i-1,v],F[i,v-C_{i}]}\} 3171 | \] 3172 | 3173 | \end_inset 3174 | 3175 | 3176 | \end_layout 3177 | 3178 | \begin_layout Standard 3179 | 初始条件是 3180 | \begin_inset Formula $F[0,0]=1$ 3181 | \end_inset 3182 | 3183 | 。 3184 | \end_layout 3185 | 3186 | \begin_layout Standard 3187 | 事实上,这样做可行的原因在于状态转移方程已经考察了所有可能的背包组成方案。 3188 | \end_layout 3189 | 3190 | \begin_layout Subsection 3191 | 最优方案的总数 3192 | \end_layout 3193 | 3194 | \begin_layout Standard 3195 | 这里的最优方案是指物品总价值最大的方案。以01背包为例。 3196 | \end_layout 3197 | 3198 | \begin_layout Standard 3199 | 结合求最大总价值和方案总数两个问题的思路,最优方案的总数可以这样求: 3200 | \begin_inset Formula $F[i,v]$ 3201 | \end_inset 3202 | 3203 | 代表该状态的最大价值, 3204 | \begin_inset Formula $G[i,v]$ 3205 | \end_inset 3206 | 3207 | 表示这个子问题的最优方案的总数,则在求 3208 | \begin_inset Formula $F[i,v]$ 3209 | \end_inset 3210 | 3211 | 的同时求 3212 | \begin_inset Formula $G[i,v]$ 3213 | \end_inset 3214 | 3215 | 的伪代码如下: 3216 | \end_layout 3217 | 3218 | \begin_layout LyX-Code 3219 | \begin_inset Formula $G[0,0]\,\leftarrow1$ 3220 | \end_inset 3221 | 3222 | 3223 | \end_layout 3224 | 3225 | \begin_layout LyX-Code 3226 | for 3227 | \begin_inset Formula $i\,\leftarrow1$ 3228 | \end_inset 3229 | 3230 | to 3231 | \begin_inset Formula $N$ 3232 | \end_inset 3233 | 3234 | 3235 | \end_layout 3236 | 3237 | \begin_layout LyX-Code 3238 | for 3239 | \begin_inset Formula $v\,\leftarrow0$ 3240 | \end_inset 3241 | 3242 | to 3243 | \begin_inset Formula $V$ 3244 | \end_inset 3245 | 3246 | 3247 | \end_layout 3248 | 3249 | \begin_layout LyX-Code 3250 | 3251 | \begin_inset Formula $F[i,v]\,\leftarrow\mathrm{max}\{F[i-1,v],F[i-1,v-C_{i}]+W_{i}\}$ 3252 | \end_inset 3253 | 3254 | 3255 | \end_layout 3256 | 3257 | \begin_layout LyX-Code 3258 | 3259 | \begin_inset Formula $G[i,v]\,\leftarrow0$ 3260 | \end_inset 3261 | 3262 | 3263 | \end_layout 3264 | 3265 | \begin_layout LyX-Code 3266 | if 3267 | \family roman 3268 | \series medium 3269 | \shape up 3270 | \size normal 3271 | \emph off 3272 | \bar no 3273 | \strikeout off 3274 | \uuline off 3275 | \uwave off 3276 | \noun off 3277 | \color none 3278 | \lang english 3279 | 3280 | \begin_inset Formula $F[i,v]=F[i-1,v]$ 3281 | \end_inset 3282 | 3283 | 3284 | \end_layout 3285 | 3286 | \begin_layout LyX-Code 3287 | 3288 | \begin_inset Formula $G[i,v]\,\leftarrow G[i,v]+G[i-1][v]$ 3289 | \end_inset 3290 | 3291 | 3292 | \end_layout 3293 | 3294 | \begin_layout LyX-Code 3295 | if 3296 | \begin_inset Formula $F[i,v]=F[i-1,v-C_{i}]+W_{i}$ 3297 | \end_inset 3298 | 3299 | 3300 | \end_layout 3301 | 3302 | \begin_layout LyX-Code 3303 | 3304 | \begin_inset Formula $G[i,v]\,\leftarrow G[i,v]+G[i-1][v-C_{i}]$ 3305 | \end_inset 3306 | 3307 | 3308 | \end_layout 3309 | 3310 | \begin_layout Standard 3311 | 如果你是第一次看到这样的问题,请仔细体会上面的伪代码。 3312 | \end_layout 3313 | 3314 | \begin_layout Subsection 3315 | 求次优解、第 3316 | \begin_inset Formula $K$ 3317 | \end_inset 3318 | 3319 | 优解 3320 | \end_layout 3321 | 3322 | \begin_layout Standard 3323 | 对于求次优解、第 3324 | \begin_inset Formula $K$ 3325 | \end_inset 3326 | 3327 | 优解类的问题,如果相应的最优解问题能写出状态转移方程、用动态规划解决,那么求次优解往往可以相同的复杂度解决,第 3328 | \begin_inset Formula $K$ 3329 | \end_inset 3330 | 3331 | 优解则比求最优解的复杂度上多一个系数 3332 | \begin_inset Formula $K$ 3333 | \end_inset 3334 | 3335 | 。 3336 | \end_layout 3337 | 3338 | \begin_layout Standard 3339 | 其基本思想是,将每个状态都表示成有序队列,将状态转移方程中的 3340 | \begin_inset Formula $\mathrm{max/min}$ 3341 | \end_inset 3342 | 3343 | 转化成有序队列的合并。 3344 | \end_layout 3345 | 3346 | \begin_layout Standard 3347 | 这里仍然以01背包为例讲解一下。 3348 | \end_layout 3349 | 3350 | \begin_layout Standard 3351 | 首先看01背包求最优解的状态转移方程: 3352 | \begin_inset Formula $F[i,v]=\mathrm{max}\{F[i-1,v],F[i-1,v-C_{i}]+W_{i}\}$ 3353 | \end_inset 3354 | 3355 | 。如果要求第 3356 | \begin_inset Formula $K$ 3357 | \end_inset 3358 | 3359 | 优解,那么状态 3360 | \begin_inset Formula $F[i,v]$ 3361 | \end_inset 3362 | 3363 | 就应该是一个大小为 3364 | \begin_inset Formula $K$ 3365 | \end_inset 3366 | 3367 | 的队列 3368 | \begin_inset Formula $F[i,v,1\ldots K]$ 3369 | \end_inset 3370 | 3371 | 。其中 3372 | \begin_inset Formula $F[i,v,k]$ 3373 | \end_inset 3374 | 3375 | 表示前 3376 | \begin_inset Formula $i$ 3377 | \end_inset 3378 | 3379 | 个物品中,背包大小为 3380 | \begin_inset Formula $v$ 3381 | \end_inset 3382 | 3383 | 时,第 3384 | \begin_inset Formula $k$ 3385 | \end_inset 3386 | 3387 | 优解的值。这里也可以简单地理解为在原来的方程中加了一维来表示结果的优先次序。显然 3388 | \begin_inset Formula $f[i,v,1\ldots K]$ 3389 | \end_inset 3390 | 3391 | 这 3392 | \begin_inset Formula $K$ 3393 | \end_inset 3394 | 3395 | 个数是由大到小排列的,所以它可看作是一个有序队列。 3396 | \end_layout 3397 | 3398 | \begin_layout Standard 3399 | 然后原方程就可以解释为: 3400 | \begin_inset Formula $F[i,v]$ 3401 | \end_inset 3402 | 3403 | 这个有序队列是由 3404 | \begin_inset Formula $F[i-1,v]$ 3405 | \end_inset 3406 | 3407 | 和 3408 | \begin_inset Formula $F[i-1\text{,}v-C_{i}]+W_{i}$ 3409 | \end_inset 3410 | 3411 | 这两个有序队列合并得到的。前者 3412 | \begin_inset Formula $F[i-1][V]$ 3413 | \end_inset 3414 | 3415 | 即 3416 | \begin_inset Formula $F[i-1,v,1\ldots K]$ 3417 | \end_inset 3418 | 3419 | ,后者 3420 | \begin_inset Formula $F[i-1\text{,}v-C_{i}]+W_{i}$ 3421 | \end_inset 3422 | 3423 | 则理解为在 3424 | \begin_inset Formula $F[i-1\text{,}v-C_{i},1\ldots K]$ 3425 | \end_inset 3426 | 3427 | 的每个数上加上 3428 | \begin_inset Formula $W_{i}$ 3429 | \end_inset 3430 | 3431 | 后得到的有序队列。合并这两个有序队列并将结果的前 3432 | \begin_inset Formula $K$ 3433 | \end_inset 3434 | 3435 | 项储存到 3436 | \begin_inset Formula $f[i,v,1\ldots K]$ 3437 | \end_inset 3438 | 3439 | 中的复杂度是 3440 | \begin_inset Formula $O(K)$ 3441 | \end_inset 3442 | 3443 | 。最后的第 3444 | \begin_inset Formula $K$ 3445 | \end_inset 3446 | 3447 | 优解的答案是 3448 | \begin_inset Formula $F[N,V,K]$ 3449 | \end_inset 3450 | 3451 | 。总的时间复杂度是 3452 | \begin_inset Formula $O(VNK)$ 3453 | \end_inset 3454 | 3455 | 。 3456 | \end_layout 3457 | 3458 | \begin_layout Standard 3459 | 为什么这个方法正确呢?实际上,一个正确的状态转移方程的求解过程遍历了所有可用的策略,也就覆盖了问题的所有方案。只不过由于是求最优解,所以其它在任何一个策略上达不 3460 | 到最优的方案都被忽略了。如果把每个状态表示成一个大小为 3461 | \begin_inset Formula $K$ 3462 | \end_inset 3463 | 3464 | 的数组,并在这个数组中有序地保存该状态可取到的前 3465 | \begin_inset Formula $K$ 3466 | \end_inset 3467 | 3468 | 个最优值。那么,对于任两个状态的 3469 | \begin_inset Formula $\mathrm{max}$ 3470 | \end_inset 3471 | 3472 | 运算等价于两个由大到小的有序队列的合并。 3473 | \end_layout 3474 | 3475 | \begin_layout Standard 3476 | 另外还要注意题目对于“第 3477 | \begin_inset Formula $K$ 3478 | \end_inset 3479 | 3480 | 优解”的定义,是要求将策略不同但权值相同的两个方案是看作同一个解还是不同的解。如果是前者,则维护有序队列时要保证队列里的数没有重复的。 3481 | \end_layout 3482 | 3483 | \begin_layout Subsection 3484 | 小结 3485 | \end_layout 3486 | 3487 | \begin_layout Standard 3488 | 显然,这里不可能穷尽背包类动态规划问题所有的问法。甚至还存在一类将背包类动态规划问题与其它领域(例如数论、图论)结合起来的问题,在这篇论背包问题的专文中也不会论 3489 | 及。但只要深刻领会前述所有类别的背包问题的思路和状态转移方程,遇到其它的变形问题,应该也不难想出算法。 3490 | \end_layout 3491 | 3492 | \begin_layout Standard 3493 | 触类旁通、举一反三,应该也是一个程序员应有的品质吧。 3494 | \end_layout 3495 | 3496 | \end_body 3497 | \end_document 3498 | --------------------------------------------------------------------------------