一、是什么?
首先来看一个集合覆盖问题:
假如存在下面需要付费的广播台,以及广播台信号可以覆盖的地区,如何选择最少的广播台,让所有地区都可以接收到信号?
广播台 | 覆盖地区 |
---|---|
k1 | 北京、上海、天津 |
k2 | 北京、广州、深圳 |
k3 | 上海、成都、杭州 |
k4 | 上海、天津 |
k5 | 杭州、大连 |
也就是说现在地区总共有8个,即北京、上海、天津、广州、深圳、成都、杭州、大连,如何订购最少的广播台,可以收听到这8个地区的广播。
这个问题就是经典的用贪心算法求解的问题。贪心算法是指在每一步选择中都采取最优的策略,从而希望能够导致结果是最优的一种算法。贪心算法所得到的结果并不一定是最优的解,但都是相对接近最优解的结果。
二、案例:
要解决上面的问题,该怎么做呢?常规的做法如下:
2^5 = 32
中组合。怎么来的?就是5个数不考虑顺序进行排列组合嘛。这样做显然很麻烦,要是有100个广播台,那不是完犊子了。但是可以使用贪心算法,提高效率。贪心算法步骤如下:
如果用上面的案例来说的话,那么步骤就是:
所以最终的选择结果是k1、k2、k3、k5。
三、代码实现:
将上面的问题用代码实现出来。
public class GreedyDemo {
public static void main(String[] args) {
// 广播电台及其对应覆盖地区用map保存
Map<String, Set<String>> map = new HashMap<>();
Set<String> areaSet1 = new HashSet<>();
areaSet1.add("北京");
areaSet1.add("上海");
areaSet1.add("天津");
Set<String> areaSet2 = new HashSet<>();
areaSet2.add("北京");
areaSet2.add("广州");
areaSet2.add("深圳");
Set<String> areaSet3 = new HashSet<>();
areaSet3.add("上海");
areaSet3.add("成都");
areaSet3.add("杭州");
Set<String> areaSet4 = new HashSet<>();
areaSet4.add("上海");
areaSet4.add("天津");
Set<String> areaSet5 = new HashSet<>();
areaSet5.add("杭州");
areaSet5.add("大连");
map.put("k1", areaSet1);
map.put("k2", areaSet2);
map.put("k3", areaSet3);
map.put("k4", areaSet4);
map.put("k5", areaSet5);
System.out.println(greedy(map));;
}
public static List<String> greedy(Map<String, Set<String>> map){
// 遍历map,拿到所有地区,保存起来
Set<String> allArea = new HashSet<>();
for(String key : map.keySet()) {
allArea.addAll(map.get(key));
}
// 用来保存所选电台的集合
List<String> selected = new ArrayList<>();
Set<String> temp = new HashSet<>();
String selectedKey = null;
while (allArea.size() != 0) {
for (String key : map.keySet()) {
temp.clear();
selectedKey = null;
Set<String> area = map.get(key);
temp.addAll(area);
// 跟allArea求交集
temp.retainAll(allArea);
if (temp.size() > 0 && (selectedKey == null || temp.size() > map.get(selectedKey).size())) {
selectedKey = key;
}
// 找到了当前这一轮选择的广播台
if (selectedKey != null) {
selected.add(selectedKey);
allArea.removeAll(map.get(selectedKey));
}
}
}
return selected;
}
}