题目出处点这里
思路:先把结束时间end从小到大排序,再按end时间从小到大参加比赛
约束条件:
1、前一个参加的比赛的end时间必须小于等于后一个参加比赛的start时间
2、前一个参加比赛的end时间必须小于后一个参加的比赛end时间
代码(解法一)如下:
两个判断的先后顺序略微会影响运行时间
可能是因为洛谷的测试数据的end时间没有重复的
所以在洛谷里把第二个约束条件注释掉也可以
后面还有解法二,我觉得对数据量大且end重复较多时的测试数据起到比较好的效果,
但在洛谷测试数据会很慢
package greedy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Scanner;
public class P1803_plus {
static ArrayList<time> list = new ArrayList<time>();// 存放time的队列
static int sum = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
// 存放time对象,同时把end的个数++
for (int i = 0; i < n; i++) {
list.add(new time(sc.nextInt(), sc.nextInt()));
}
// 按照end升序排列
Collections.sort(list, new Comparator<time>() {
public int compare(time a, time b) {
return a.end - b.end;// 代表升序
}
});
// 默认参加了排序后的第一个比赛(因为对于第一个参加的比赛来说,只要end相等,参加哪一个都无所谓),所以后面的i从1开始判断,好写代码
sum = 1;
// 用来记录上一个参加的比赛的索引,默认就是第一个比赛(因为已经排序好)
int theLast = 0;
for (int i = 1; i < n; i++) {// i从1开始,好写代码
if (list.get(i).start >= list.get(theLast).end) {// 其次现在的start必须大于等于上一个比赛的end
if (list.get(i).end > list.get(theLast).end) {// 首先现在的end必须大于上一个参加的比赛的end,否则会造成重复
sum++;// 以上两个条件都满足的话.就++
theLast = i;// 记得把上一个参加的比赛的索引变成现在已经参加过了的比赛i
}
}
}
System.out.println(sum);
}
}
class time {
int start;
int end;
public time(int start, int end) {
this.start = start;
this.end = end;
}
}
代码(解法二):
此解法适用于数据量比较大且end重复较多的情况
思路:
1、用endCount[]记录每个end的个数,并把end的种类从大到小存放到endType[]中
2、这样就只需要经过endType.length次大循环
3、每次大循环把不同种类的end遍历一遍,只要这一次的start大于等于上一次的end,就次数++后break
4、这种方法大大减少了解法一面对大量重复end时所需要的判断时间,因为在end重复量多即end种类少
的时候需要经过的大循环就很少,每次大循环需要判断的次数也很少,因此此解法适用于数据量比较大
且end重复较多的情况
5、但是在数据量大且end几乎不重复的情况下此解法时间会比较长,因为每次都需要找到不同end种类的
索引范围,浪费了大量的时间并且需要经过的大循环的次数几乎就等于n,因此此解法不适用与数据量
大且end几乎不重复的情况
package greedy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Scanner;
public class P1803 {
static ArrayList<time> list = new ArrayList<time>();//存放time的队列
static ArrayList<Integer> endList = new ArrayList<Integer>();//存放end的队列
static int[] endCount = new int[1000001];// 存放各个end分别出现的次数
static ArrayList<Integer> endType = new ArrayList<Integer>(); // 存放存在的end类型
static int index = 0;
static int sum;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
// 存放time对象,同时把end的个数++
for (int i = 0; i < n; i++) {
int start = sc.nextInt();
int end = sc.nextInt();
list.add(new time(start, end));
endList.add(end);
endCount[end]++;
}
// 找出存在的end类型s
for (int i = 0; i < endCount.length; i++) {
if (endCount[i] > 0) {// 如果end类的数量大于0,就说明存在此类型
endType.add(i);// 然后把i加进去即可
}
}
// 按照end升序排列
Collections.sort(list, new Comparator<time>() {
public int compare(time a, time b) {
return a.end - b.end;// 代表升序
}
});
Collections.sort(endList);//对endList也进行升序排列
// 排序完后先+1,代表已经选择了第一种end
sum++;
//theLast代表上一个可以参加的比赛的end在endType中的索引
int theLast = 0;
// 取出第二种end类型,如果此种end对应的start有 大于等于 第一种end的就+1
int startIndex = 0;
int endIndex = 0;
for (int i = 1; i < endType.size(); i++) {
startIndex = endList.indexOf(endType.get(i));
endIndex = endList.lastIndexOf(endType.get(i));
for (int j = startIndex; j <= endIndex; j++) {
if (list.get(j).start >= endType.get(theLast)) {
theLast = i;
sum++;
break;
}
}
}
System.out.println(sum);
}
}
class time {
int start;
int end;
public time(int start, int end) {
this.start = start;
this.end = end;
}
}