1、问题抛出
快速选用最少的广播台覆盖需要的地市
states_needed = set(['mt','wa', 'or', 'id', 'nv', 'ut', 'ca', 'az'])
stations = {}
stations['kone'] = set(['id', 'nv', 'ut'])
stations['ktwo'] = set(['id', 'wa', 'mt'])
stations['kthree'] = set(['or', 'nv', 'ca'])
stations['kfour'] = set(['nv', 'ut'])
stations['kfive'] = set(['ca', 'az'])
2、思路
1) 最简单粗暴的想法是,直接对广播台进行排列组合,对比后选出最小的组合
当有n个广播台的时候,总共组合:
C
n
1
+
C
n
2
+
C
n
3
+
.
.
.
+
C
n
n
C_n^1 +C_n^2 +C_n^3 + ... + C_n^n
Cn1+Cn2+Cn3+...+Cnn
当 n 为奇数时可化简为:
(
C
n
0
+
C
n
n
−
1
2
)
∗
n
+
1
4
∗
2
−
C
n
0
(C_n^0 +C_n^\frac{n-1}{2}) * \frac{n+1}{4} * 2 - C_n^0
(Cn0+Cn2n−1)∗4n+1∗2−Cn0
即
[
n
−
1
+
C
n
n
−
1
2
∗
(
n
+
1
)
]
/
2
[n -1 +C_n^\frac{n-1}{2}* (n+1)] / 2
[n−1+Cn2n−1∗(n+1)]/2
当 n 为偶数时可化简为:
(
C
n
0
+
C
n
n
−
2
2
)
∗
n
4
∗
2
−
C
n
0
+
C
n
n
/
2
(C_n^0 +C_n^\frac{n-2}{2}) * \frac{n}{4} * 2 - C_n^0 + C_{n}^{n/2}
(Cn0+Cn2n−2)∗4n∗2−Cn0+Cnn/2
即
[
n
−
2
+
C
n
n
−
2
2
∗
n
+
2
C
n
n
/
2
]
/
2
[n -2 +C_n^\frac{n-2}{2}* n + 2C_{n}^{n/2}] / 2
[n−2+Cn2n−2∗n+2Cnn/2]/2
可见其组合会随着n的增加迅速增长,肯定不是处理的好方式
2)近似求解
1.先选出一个广播台,即它覆盖最多的地市。即便这个广播覆盖部分有重复。
2.然后用该地市和需要的地市的集合做差集,
3.重复前两步,直到差集为空。
每查找覆盖面最广的广播站需要n次,最多查找n遍,所以最极端情况也仅需要 n 2 n^2 n2 次。
3、算法实现
def Tan(stations, needed):
import copy
states_needed = copy.deepcopy(needed) # 保证不修改原数据
final_stations = set()
while states_needed:
best_station = None
states_covered = set()
# 循环寻找覆盖面最广的广播站
for station, states in stations.items():
covered = states_needed & states # & 交集 | 并集 - 差集
if len(covered) > len(states_covered):
best_station = station
states_covered = covered
states_needed -= states_covered # 差集
final_stations.add(best_station) # 需要的广播站
return final_stations
Tan(stations, states_needed)
结果
{'kthree', 'kfive', 'ktwo', 'kone'}
该算法可以快速找到近似解,在很多时候,并不需要最准确的答案,最接近准确的答案的解也是可接受的,且时间成本更低。