贪心算法【区间调度】【集合覆盖】【背包问题】【旅行商问题】【哈夫曼构造价值树】

贪心算法

 在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
 贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。

1.区间调度问题

假设有如下课程,希望尽可能多的将课程安排在一间教室里:
|课程|开始时间 |结束时间
|–|–|–|
| 美术 | 9AM |10AM|
英语|9:30AM|10:30AM|
数学|10AM|11AM|
计算机|10:30AM|11:30AM|
音乐|11AM|12AM|
算法设计:

  • 1.选择结束最早的课,便是要在这教室上课的第一节课
  • 2.接下来,选择第一堂课结束后才开始的课,并且结束最早的课,这将是第二节在教室上的课。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import java.util.*;
public class Main{
public static void main(String[] args) {
String subject[] = {"英语","数学","计算机","音乐","美术"};
Work[] works = {
new Work(1,3),
new Work(2, 5),
new Work(4, 7),
new Work(6, 9),
new Work(8, 10)
};
int result = solution(works);
System.out.println(result);
}
private static int solution(Work[] works) {
//works里面已经按end从小到大排序了
Arrays.sort(works);
int count = 0;
//当前工作的结束时间
int endTime = 0;
for(int i = 0;i<works.length;i++){
if(endTime<works[i].getStart()){
count++;
endTime = works[i].getEnd();
}
}
return count;
}
static class Work implements Comparable{
private int start;
private int end;
public Work(int start, int end) {
this.start = start;
this.end = end;
}
//end 从小到大排序
@Override
public int compareTo(Object o) {
Work work = (Work) o;
if(this.end > work.getEnd()){
return 1;
}else if(this.end == work.getEnd()){
return 0;
}else{
return -1;
}
}
public int getStart() {
return start;
}
public int getEnd() {
return end;
}
}
}

相同问题:2019届爱奇艺校招笔试题第三题_库特君的面条:   https://blog.csdn.net/qq_17556191/article/details/95003363

2.背包问题

见:https://blog.csdn.net/qq_17556191/article/details/94764606

3.集合覆盖问题

假设存在如下表的需要付费的广播台,以及广播台信号可以覆盖的地区。 如何选择最少的广播台,让所有的地区都可以接收到信号。
|广播台|覆盖地区 |
|–|–|
| K1 |ID,NV,UT |
K2|WA,ID,MT|
K3|OR,NV,CA|
K4|NV,UT|
K5|CA,AZ|
…|…|
算法设计:
(1) 选出一个广播台,即它覆盖了最多未覆盖的地区即便包含一些已覆盖的地区也没关系
(2) 重复第一步直到覆盖了全部的地区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import java.util.*;
public class Main_tanxin_jihefugai {
public static void main(String[] args) {
HashMap<String,HashSet<String>> broadcasts = new HashMap<>();
broadcasts.put("K1", new HashSet(Arrays.asList(new String[] {"ID","NV","UT"})));
broadcasts.put("K2", new HashSet(Arrays.asList(new String[] {"WA","ID","MT"})));
broadcasts.put("K3", new HashSet(Arrays.asList(new String[] {"OR","NV","CA"})));
broadcasts.put("K4", new HashSet(Arrays.asList(new String[] {"NV","UT"})));
broadcasts.put("K5", new HashSet(Arrays.asList(new String[] {"CA","AZ"})));
//需要覆盖的全部地区
HashSet<String> allAreas = new HashSet<>(Arrays.asList(new String[] {"ID","NV","UT","WA","MT","OR","CA","AZ"}));
//所选择的广播台列表
List<String> lists = new ArrayList<>();
//(1) 选出一个广播台,即它覆盖了最多未覆盖的地区即便包含一些已覆盖的地区也没关系
// (2) 重复第一步直到可以覆盖了全部的地区
HashSet<String> tempSet = new HashSet<>();
String maxKey = null;
while(allAreas.size()!=0){
maxKey = null;
//一轮轮选出(之前未覆盖的)数量最多的电台
for(String key:broadcasts.keySet()){
tempSet.clear();
HashSet<String> areas = broadcasts.get(key);
tempSet.addAll(areas);
//求出2个集合的交集,得到的交集的电台存在tempSet里面
tempSet.retainAll(allAreas);
//如果该集合包含的地区数量比原本的集合多
if (tempSet.size()>0 && (maxKey == null || tempSet.size() > broadcasts.get(maxKey).size())) {
//得到要存入的电台key
maxKey = key;
}

}
//找到未覆盖电台数的最大的那个电台
if (maxKey != null) {
lists.add(maxKey);
//将覆盖地区移除allAreas
allAreas.removeAll(broadcasts.get(maxKey));
}
}
System.out.print("selects:" + lists);

}
}

4.旅行商问题

 给定一系列城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路。
算法设计:
随机选一个城市开始,以寻找离该城市最近点作为下一个城市旅行城市,此后寻找离最后加入路线的城市最近的城市,直到最后。【类似于广度优先遍历】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//旅行商问题
public class Main {
public static void main(String[] args) {
String city[] = {"A","B","C","D","E","F","G","H","I","J"};
int x[] = {2066,935,1270,1389,984,2253,949,87,3094,2706};
int y[] = {2333,1304,200,700,2810,478,3025,2483,1883,3130};
double distance[][] = Caldistance(x,y);
solve(distance,city);
}
//计算距离
public static double[][] Caldistance(int x[],int y[]){
double [][] distance = new double[x.length][x.length];
for(int i = 0;i<x.length;i++){
for(int j = 0;j<x.length;j++){
//欧式距离
distance[i][j] = Math.sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
}
return distance;
}
public static void solve(double[][]distance,String []city){
double sum = 0;
//记录访问过的城市
int visited[] = new int[distance.length];
//记录依次访问的城市,得到最小的距离
String path[] = new String[distance.length];
//假设从第一个城市A开始
path[0] = "A";
//访问过的点记录为1
visited[0] = 1;
//当前正在访问的点
int k = 0;
//第一次设下一个城市为Index为1的B城市
int nextCityIndex = 1;
//初始化最小的距离
double mindistance = Double.MAX_VALUE;
//记录以及访问过多少个城市了
int count = 1;
while(count<city.length){
for(int i = 0;i<city.length;i++){
//如果该点没被访问
if(visited[i]==0){
//求出 从正在访问的点k到下一个点的最小距离,即为下一个要被访问的城市
if(distance[k][i]<mindistance){
mindistance = distance[k][i];
//获取下一个要被访问的城市的下标索引
nextCityIndex = i;
}
}
}
//依次访问的点的总路程
sum = sum + mindistance;
//将得到的下一个要访问的城市加入到path中
path[count++] = city[nextCityIndex];
//该点被访问,置为1
visited[nextCityIndex] = 1;
//将访问的城市起点改成求得的下一个城市的点的坐标索引
k = nextCityIndex;
//再次初始化
mindistance = Double.MAX_VALUE;
}
//得出依次访问的结果,输出
for(int i = 0;i<path.length;i++){
System.out.print(path[i]+" ");
}
}
}

5.哈夫曼构造价值树

哈夫曼见: https://blog.csdn.net/likunkun__/article/details/80258515
相同题型:切金条: https://blog.csdn.net/qq_34115899/article/details/79723970

参考:图解贪婪算法 https://blog.csdn.net/a8082649/article/details/82079779

文章目录
  1. 1. 贪心算法
  2. 2. 1.区间调度问题
  3. 3. 2.背包问题
  4. 4. 3.集合覆盖问题
  5. 5. 4.旅行商问题
  6. 6. 5.哈夫曼构造价值树
| 139.6k