@@ -1498,3 +1498,335 @@ B+树是B树的一种变种和改进,在数据库系统中应用更加广泛
14981498
14991499由于B+树这些优势,大多数现代数据库系统(如MySQL的InnoDB引擎、PostgreSQL等)都采用B+树作为索引结构,特别适合处理大量的范围查询和排序操作。
15001500
1501+
1502+ # ## 贪心集合覆盖
1503+
1504+ 每个广播站都覆盖一个或者多个州市,广播站的覆盖范围可能会有重复。如何设计一个算法,找到最少广播站,覆盖所有州市?
1505+
1506+ 这是一个典型的** 集合覆盖** 问题,可以使用贪心算法来解决。
1507+
1508+ 贪婪算法寻找局部最优解,可能不是全局最优解(例如背包问题),但通常情况下,贪婪算法的结果已经足够接近最优解。
1509+
1510+ 数据示例:
1511+
1512+ ` ` ` python showLineNumbers
1513+ stations = {
1514+ ' kone' : {' id' , ' nv' , ' ut' },
1515+ ' ktwo' : {' wa' , ' id' , ' mt' },
1516+ ' kthree' : {' or' , ' nv' , ' ca' },
1517+ ' kfour' : {' nv' , ' ut' },
1518+ ' kfive' : {' ca' , ' az' },
1519+ }
1520+ ` ` `
1521+
1522+ # ### 思路
1523+
1524+ 正确的解可能有多个,你需要遍历所有未选择的广播站,从中选择一个最多未覆盖的广播站。
1525+
1526+ # ### 题解
1527+
1528+ ` ` ` python showLineNumbers
1529+ stations = {
1530+ ' kone' : {' id' , ' nv' , ' ut' },
1531+ ' ktwo' : {' wa' , ' id' , ' mt' },
1532+ ' kthree' : {' or' , ' nv' , ' ca' },
1533+ ' kfour' : {' nv' , ' ut' },
1534+ ' kfive' : {' ca' , ' az' },
1535+ }
1536+
1537+ # 最终选择的广播站
1538+ final_stations = set ()
1539+
1540+ # 获取所有州
1541+ states_needed = set.union(*stations.values ())
1542+ print(states_needed)
1543+ # {'mt', 'az', 'nv', 'wa', 'id', 'ut', 'ca', 'or'}
1544+
1545+ # 如果还有未覆盖的州,则继续选择最好的广播站
1546+ while states_needed :
1547+ # 当前最好的选择
1548+ best_station = None
1549+ # 当前最好的选择覆盖的州
1550+ states_covered = set ()
1551+ for station, states in stations.items ():
1552+ # 计算当前广播站与还需覆盖的州的交集
1553+ covered = states & states_needed
1554+ # 如果此交集比 states_covered 大,
1555+ if len(covered) > len(states_covered):
1556+ # 则更新当前最好的选择
1557+ best_station = station
1558+ # 则更新覆盖的州为当前交集
1559+ states_covered = covered
1560+ # 需要覆盖的州减去当前最好的选择的覆盖的州
1561+ states_needed -= states_covered
1562+ # 将当前最好的选择添加到最终选择的广播站中
1563+ print(best_station)
1564+ final_stations.add(best_station)
1565+ " " "
1566+ kone
1567+ ktwo
1568+ kthree
1569+ kfive
1570+ " " "
1571+ ` ` `
1572+
1573+ # ## 背包问题
1574+
1575+ 假设你有一个4磅的背包,需要在容量范围内,选择总价值最高的物品放入背包。
1576+
1577+ ` ` ` python showLineNumbers
1578+ items = {
1579+ " 音响" :{' 重量' :4, ' 价值' :3000},
1580+ " 笔记本电脑" :{' 重量' :3, ' 价值' :2000},
1581+ " 吉他" :{' 重量' :1, ' 价值' :1500},
1582+ " iphone" :{' 重量' :1, ' 价值' :2000},
1583+ }
1584+ ` ` `
1585+
1586+ 暴力求解的复杂度为$$ 2^n$$ ,其中n是物品数量。
1587+
1588+ # ### 思路
1589+
1590+ 这题属于动态规划问题,首先定义一个二维表格。
1591+
1592+ | 物品名称| 1| 2| 3| 4|
1593+ | ---| ---| ---| ---| ---|
1594+ | 音响| | | | |
1595+ | 笔记本电脑| | | | |
1596+ | 吉他| | | | |
1597+ | iphone| | | | |
1598+
1599+ 这个表格的右上角表示:当只有音响可以选择时,且背包容量为1时,可选的最大价值。
1600+
1601+ 横向增加说明在当前可选择的物品中,背包容量增加。
1602+ 纵向增加说明在当前背包容量下,可选择的物品增加。
1603+
1604+ 所以我们可以从左上角开始,逐步填充表格。
1605+
1606+ 由于音响的重量为4,所以当背包容量为1 ~ 3时,无法选择音响。最大价值为0。当背包容量为4时,可以选择音响,最大价值为3000。
1607+
1608+ | 物品名称| 1| 2| 3| 4|
1609+ | ---| ---| ---| ---| ---|
1610+ | 音响| 0 | 0 | 0 | 3000|
1611+ | 笔记本电脑| | | | |
1612+ | 吉他| | | | |
1613+ | iphone| | | | |
1614+
1615+ 当来到第二行时,可以选择笔记本电脑或音响。其中笔记本电脑重量为3,所以当背包容量为1 ~ 2时,无法选择笔记本电脑。最大价值为0。
1616+
1617+ 当背包容量为3时,可以选择笔记本电脑,最大价值为2000。当背包容量为4时,可以选择笔记本电脑或音响,由于音响的价值更高,所以选择音响,最大价值为3000。
1618+
1619+ | 物品名称| 1| 2| 3| 4|
1620+ | ---| ---| ---| ---| ---|
1621+ | 音响| 0 | 0 | 0 | 3000|
1622+ | 笔记本电脑| 0 | 0 | 2000 | 3000|
1623+ | 吉他| | | | |
1624+ | iphone| | | | |
1625+
1626+ 当来到第三行时,可以选择吉他、笔记本电脑或音响。
1627+
1628+ 当背包重量为1~2时,选吉他,最大价值为1500。不选则为0。所以应该选择,此时最大价值为1500。
1629+
1630+ 你会发现从上往下,每行都比上一行多一个物品。且对应的最大价值不会变小(只会变大或者与上一行相等)
1631+
1632+ 目前已经更新的表的最后一行,就表示当前容量下,可选物品的最大价值。
1633+
1634+ 且从左向右,每列都比前一格容量更多,所以对应的最大价值也不会变小(只会变大或者与上一行相等)
1635+
1636+ 最终右下角的值,就是整个表最大的值之一(因为可能会有多个相等的最大值)
1637+
1638+ | 物品名称| 1| 2| 3| 4|
1639+ | ---| ---| ---| ---| ---|
1640+ | 音响| 0 | 0 | 0 | 3000|
1641+ | 笔记本电脑| 0 | 0 | 2000 | 3000|
1642+ | 吉他| 1500 | 1500 | X | Y |
1643+ | iphone| | | | |
1644+
1645+ 所以当填写 " X" 时,我们< Highlight> 向上数一格< /Highlight> ,发现至少有一个价值2000的选择。
1646+
1647+ 接下来比较其他可选项:吉他(1500)、或者不选(0)。取最大值,所以填写2000。
1648+
1649+ 当我们填写 " Y" 时,我们< Highlight> 向左看< /Highlight> ,发现" Y" 只比左侧的" X" 增加< Highlight> 一个< /Highlight> 空间。< Highlight> 向上看< /Highlight> ,发现之前的最佳选择是3000。
1650+
1651+ 所以我们计算 容量3的最大值 + 容量1的最大值 = 2000 + 1500 = 3500
1652+
1653+ 和历史最佳3000取最大值,所以应该填 3500
1654+
1655+ | 物品名称| 1| 2| 3| 4|
1656+ | ---| ---| ---| ---| ---|
1657+ | 音响| 0 | 0 | 0 | 3000|
1658+ | 笔记本电脑| 0 | 0 | 2000 | 3000|
1659+ | 吉他| 1500 | 1500 | 2000 | 3500 |
1660+ | iphone| | | | |
1661+
1662+ 最后一层,我们尝试编写伪代码
1663+
1664+ < Highlight> 每下移一行,我们就会多了一个物品可以选择。所以这一行的每个判断都是围绕着是否选择这个新增物品展开的。< /Highlight>
1665+
1666+ 首先判断当前背包空间是否大于等于这个物品的空间。如果是否则直接沿用上一列对应格子的值。
1667+
1668+ 其次判断如果比当前物品大,是否选择:
1669+
1670+ 如果选择这个物品,` 最大价值 = 当前物品价值 + 表格[前一行][(当前背包空间-物品空间)的值]`
1671+
1672+ 如果不选择这个物品,` 最大价值 = 表格[前一行][当前背包空间的值]`
1673+
1674+ 我们来试一下:
1675+
1676+ ` ` ` bash showLineNumbers
1677+
1678+ 当前容量 = 1
1679+
1680+ 当前背包空间是否大于等于这个物品的空间? 1 > =1 ——> 是
1681+
1682+ 如果选择这个物品:
1683+
1684+ 最大价值 = 当前物品价值(2000) + 表格[前一行][(当前背包空间-物品空间)的值](0) = 2000
1685+
1686+ 如果不选择这个物品:
1687+
1688+ 最大价值 = 表格[前一行][当前背包空间的值](1500) = 1500
1689+
1690+ 所以应该选择这个物品,最大价值为2000。
1691+ ` ` `
1692+
1693+ | 物品名称| 1| 2| 3| 4|
1694+ | ---| ---| ---| ---| ---|
1695+ | 音响| 0 | 0 | 0 | 3000|
1696+ | 笔记本电脑| 0 | 0 | 2000 | 3000|
1697+ | 吉他| 1500 | 1500 | 2000 | 3500 |
1698+ | iphone| 2000 | | | |
1699+
1700+ ` ` ` bash showLineNumbers
1701+ 当前容量 = 2
1702+
1703+ 当前背包空间是否大于等于这个物品的空间? 2 > =1 ——> 是
1704+
1705+ 如果选择这个物品:
1706+
1707+ 最大价值 = 当前物品价值(2000) + 表格[前一行][(当前背包空间-物品空间)的值](1500) = 3500
1708+
1709+ 如果不选择这个物品:
1710+
1711+ 最大价值 = 表格[前一行][当前背包空间的值](1500) = 1500
1712+
1713+ 所以应该选择这个物品,最大价值为3500。
1714+ ` ` `
1715+
1716+ | 物品名称| 1| 2| 3| 4|
1717+ | ---| ---| ---| ---| ---|
1718+ | 音响| 0 | 0 | 0 | 3000|
1719+ | 笔记本电脑| 0 | 0 | 2000 | 3000|
1720+ | 吉他| 1500 | 1500 | 2000 | 3500 |
1721+ | iphone| 2000 | 3500 | | |
1722+
1723+ ` ` ` bash showLineNumbers
1724+ 当前容量 = 3
1725+
1726+ 当前背包空间是否大于等于这个物品的空间? 3 > =1 ——> 是
1727+
1728+ 如果选择这个物品:
1729+
1730+ 最大价值 = 当前物品价值(2000) + 表格[前一行][(当前背包空间-物品空间)的值](1500) = 3500
1731+
1732+ 如果不选择这个物品:
1733+
1734+ 最大价值 = 表格[前一行][当前背包空间的值](2000) = 2000
1735+
1736+ 所以应该选择这个物品,最大价值为3500。
1737+ ` ` `
1738+ | 物品名称| 1| 2| 3| 4|
1739+ | ---| ---| ---| ---| ---|
1740+ | 音响| 0 | 0 | 0 | 3000|
1741+ | 笔记本电脑| 0 | 0 | 2000 | 3000|
1742+ | 吉他| 1500 | 1500 | 2000 | 3500 |
1743+ | iphone| 2000 | 3500 | 3500 | |
1744+
1745+
1746+ ` ` ` bash showLineNumbers
1747+ 当前容量 = 4
1748+
1749+ 当前背包空间是否大于等于这个物品的空间? 4 > =1 ——> 是
1750+
1751+ 如果选择这个物品:
1752+
1753+ 最大价值 = 当前物品价值(2000) + 表格[前一行][(当前背包空间-物品空间)的值](2000) = 4000
1754+
1755+ 如果不选择这个物品:
1756+
1757+ 最大价值 = 表格[前一行][当前背包空间的值](3500) = 3500
1758+
1759+ 所以应该选择这个物品,最大价值为4000。
1760+ ` ` `
1761+
1762+ | 物品名称| 1| 2| 3| 4|
1763+ | ---| ---| ---| ---| ---|
1764+ | 音响| 0 | 0 | 0 | 3000|
1765+ | 笔记本电脑| 0 | 0 | 2000 | 3000|
1766+ | 吉他| 1500 | 1500 | 2000 | 3500 |
1767+ | iphone| 2000 | 3500 | 3500 | 4000 |
1768+
1769+ 最后的最大值一定在左下角。(可能出现多个相等的最大值)
1770+
1771+ # ### 题解
1772+
1773+ ` ` ` python showLineNumbers
1774+ items = {
1775+ " 音响" : {' 重量' : 4, ' 价值' : 3000},
1776+ " 笔记本电脑" : {' 重量' : 3, ' 价值' : 2000},
1777+ " 吉他" : {' 重量' : 1, ' 价值' : 1500},
1778+ " iphone" : {' 重量' : 1, ' 价值' : 2000},
1779+ }
1780+ # 背包最大容量
1781+ max_weight = 4
1782+ # 物品名称
1783+ names = list(items.keys ())
1784+
1785+ # 行:物品个数,列:当前背包容量(1~max_weight)
1786+ rows = len(names)
1787+ cols = max_weight
1788+
1789+ # dp[i][w] 表示:在前 i 个物品中选择,容量为 w 时的最大价值
1790+ dp = [[0] * (cols + 1) for _ in range(rows + 1)]
1791+
1792+ # 为了让我们使用统一的状态转移方程,我们多生成了一列0和一行0,便于我们使用统一的状态转移方程。
1793+ # 表示当没有物品或没有背包空间时,最大价值为0。
1794+ # 下图中我用0.0 将其特殊标注出来
1795+ # 你从A开始逐行填写,填到Z
1796+ " " "
1797+ [[0.0, 0.0, 0.0, 0.0, 0.0],
1798+ [0.0, A, 0, 0, 0],
1799+ [0.0, 0, 0, 0, 0],
1800+ [0.0, 0, 0, 0, Z]]
1801+ " " "
1802+ for i in range(1, rows + 1):
1803+ name = names[i - 1]
1804+ weight = items[name][' 重量' ]
1805+ value = items[name][' 价值' ]
1806+ # 判断当前行的每一个背包空间是否应该选择这个物品
1807+ for w in range(1, cols + 1):
1808+ # 首先判断当前背包空间是否大于等于这个物品的空间
1809+ if w < weight:
1810+ # 如果否,则直接沿用上一行对应格子的值
1811+ dp[i][w] = dp[i - 1][w]
1812+ else:
1813+ # 如果是,则围绕「是否选择这个新增物品」展开判断
1814+ # 选择这个物品:当前物品价值 + 表格[前一行][当前背包空间-物品空间]
1815+ choose = value + dp[i - 1][w - weight]
1816+ # 不选择这个物品:表格[前一行][当前背包空间]
1817+ not_choose = dp[i - 1][w]
1818+ # 取两者中的最大值
1819+ dp[i][w] = max(choose, not_choose)
1820+
1821+ # 打印表格,便于和上面的推导对照
1822+ header = [" 物品名称" ] + [str(w) for w in range(1, cols + 1)]
1823+ print(" \t" .join(header))
1824+ for i in range(1, rows + 1):
1825+ row_name = names[i - 1]
1826+ values = [str(dp[i][w]) for w in range(1, cols + 1)]
1827+ print(row_name + " \t" + " \t" .join(values))
1828+
1829+ # 最右下角就是最大价值
1830+ max_value = dp[rows][cols]
1831+ print(" 最大总价值:" , max_value)
1832+ ` ` `
0 commit comments