集合覆盖问题——贪婪算法

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+Cn2n1)4n+12Cn0
[ n − 1 + C n n − 1 2 ∗ ( n + 1 ) ] / 2 [n -1 +C_n^\frac{n-1}{2}* (n+1)] / 2 [n1+Cn2n1(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+Cn2n2)4n2Cn0+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 [n2+Cn2n2n+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'}

该算法可以快速找到近似解,在很多时候,并不需要最准确的答案,最接近准确的答案的解也是可接受的,且时间成本更低。

你可能感兴趣的:(Python,算法)