贪心
贪心算法就是遵循某种规则,不断贪心地选取当前最有策略的算法设计方法。不从整体最优上加以考虑,它所做出的仅仅是在某种意义上的局部最优解。
贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。必须注意的是,贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性。所以,对所采用的贪心策略一定要仔细分析其是否满足无后效性。
无后效性:某个状态以前的过程不会影响以后的状态,只与当前状态有关。
基本思路
- 建立数学模型来描述问题
- 把求解的问题分成若干个子问题
- 对每个子问题求解,得到子问题的局部最优解
- 把子问题的解局部最优解合成原来问题的一个解
存在的问题
- 不能保证求得的最后解是最佳的;
- 不能用来求最大或最小解问题;
- 只能求满足某些约束条件的可行解的范围。
区间调度问题
有n项工作,没项工作分别在 si 时间开始,在 ti 时间结束。每一项工作,均可以选择参与与否。如果选择参与,必须全程参与。此外工作的时间段不能重叠(开始和结束的瞬间重叠也不允许)。
目标是完成尽可能多的工作,那么最多可以参与多少项工作呢?
限制条件
- 1<=N<=100000
- 1<=si<=ti<=109
输入
n=5,s={1,2,4,6,8},t={3,5,7,9,10}
输出
3
这个问题可以设计出好几种贪心算法:
- 每次选取开始时间最早的工作
- 每次选取结束时间最早的工作
- 每次选取用时最短的工作
- 每次选取与最少工作重叠的工作
其中“每次选取结束时间最早的工作”是正确的,其他的均有反例。
上图为算法1的反例
上图为算法3的反例
上图为算法4的反例
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
List<Work> list = new ArrayList<Work>();
for (int i = 0; i < n; i++) {
Work work = new Work();
work.setStart(sc.nextInt());
work.setEnd(sc.nextInt());
list.add(work);
}
Collections.sort(list);//按照结束时间升序排序
int count = 1;
Work tmp = list.get(0);
for (int i = 1; i < list.size(); i++) {
if (tmp.getEnd() < list.get(i).getStart()) {
count++;
}
}
System.out.println(count);
}
static class Work implements Comparable<Work> {
private int start;//开始时间
private int end;//结束
public int getEnd() {
return end;
}
public int getStart() {
return start;
}
public void setEnd(int end) {
this.end = end;
}
public void setStart(int start) {
this.start = start;
}
@Override
public int compareTo(Work work) {
return this.getEnd() - work.getEnd();
}
}
}