问题描述
大学生电影节在北大举办,这天,在北大各地放了很多部电影
给定每部电影的放映时间,区间重叠的电影不可能同时看(端点可以重合),问李雷最多可以看多少部电影。
输入
多组数据,每组数据开头是n(n<=100),表示共n场电影,接下来n行,每行两个整数(均小于1000),表示一场电影的放映区间,n=0表示数据结束
输出
对每组数据输出最多能看几部电影。
样例输入
12
1 3
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
5 10
4 14
2 9
0
样例输出
5
算法思想:
如果一部电影结束的越早,那看完这部电影之后,后面我们看的电影就越多,所以我们需要尽可能的选那种结束时间较早的电影。
贪心解法
将所有电影按结束时间从小到大排序,第一步选结束时间最早的那部电影。然后,每步都选和上一部选中的电影不冲突且结束时间最早的那部电影。
复杂度:O(nlogn)
证明:
替换法,假设用贪心法挑选的电影序列是
a1,a2,…
不用此法挑选的最长的电影序列为
b1,b2…
现可证明,对任意的I,bi均可以替换成ai
用S(x)表示x开始时间,E(x)表示x结束时间,则
1)b1可以替换成a1,因为E(a1)<=E(b1);
2)若可以找到ai满足E(ai)<=E(bi)且ai可以替换bi,则存在E(ai+1)<=E(bi+1)
证
因为E(ai)<=E(bi),且E(bi)<=S(bi+1)
则:且E(ai)<=S(bi+1)
ai是所有的S(X)>=E(ai)的x中,E(x)最小的
S(bi+1)>=E(bi)>=E(ai),所以E(bi+1)>=E(ai+1)
因此用ai+1替换bi+1不会对后续造成影响,替换可行
代码实现
package mooc;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
public class test圣诞老人的礼物 {
public class Candy implements Comparable<Candy>{
public int v;
public int w;
public Candy(int v, int w) {
super();
this.v = v;
this.w = w;
}
@Override
//重写比较方法,按照价值重量比从大到小的方式进行排序
public int compareTo(Candy o) {
if(this.v==o.v&&this.w==o.w){
return 0;
}
double result=((double)this.v/this.w)-((double)o.v/o.w);
if(result>0){
return -1;
}else{
return 1;
}
}
@Override
public String toString() {
return "Candy [v=" + v + ", w=" + w + "]";
}
}
public double MaxSumValue(int n,int weight,int v[],int w[]){
List<Candy> list=new ArrayList<>();
//将糖果的价值和重量添加到集合中去
for(int i=0;i<n;i++){
list.add(new Candy(v[i],w[i]));
}
//排序
Collections.sort(list);
//保存结果
double result=0.0;
int tmpweight=weight; //保存每次选取糖果之后可容纳的剩余重量
for(int i=0;i<n;i++){
//当可容纳的容量够大时,就把这个糖果拿走,并更新价值和剩余重量
if(list.get(i).w<tmpweight){
result+=list.get(i).v;
tmpweight-=list.get(i).w;
}else{
//当可容纳的容量不够大,就要进行拆分
result+=((double)list.get(i).v/list.get(i).w)*(tmpweight);
break;
}
}
return result;
}
@Test
public void test(){
int n=4,weight=15;
int v[]={100,412,266,591};
int w[]={4,8,7,2};
double x=MaxSumValue(n,weight,v,w);
System.out.println(x);
}
}